| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef CONTENT_BROWSER_REDUCE_ACCEPT_LANGUAGE_REDUCE_ACCEPT_LANGUAGE_UTILS_H_ |
| #define CONTENT_BROWSER_REDUCE_ACCEPT_LANGUAGE_REDUCE_ACCEPT_LANGUAGE_UTILS_H_ |
| |
| #include "content/common/content_export.h" |
| #include "content/public/browser/reduce_accept_language_controller_delegate.h" |
| #include "net/http/http_request_headers.h" |
| #include "net/http/http_response_headers.h" |
| #include "services/network/public/mojom/parsed_headers.mojom-forward.h" |
| #include "services/network/public/mojom/url_response_head.mojom.h" |
| #include "services/network/public/mojom/variants_header.mojom.h" |
| #include "third_party/blink/public/common/permissions_policy/permissions_policy.h" |
| #include "url/gurl.h" |
| |
| namespace content { |
| |
| class BrowserContext; |
| class FrameTreeNode; |
| |
| // This class is a collection of utils used by navigation requests to reduce the |
| // fingerprinting surface of the Accept-Language header. See |
| // https://github.com/Tanych/accept-language. |
| class CONTENT_EXPORT ReduceAcceptLanguageUtils { |
| public: |
| explicit ReduceAcceptLanguageUtils( |
| ReduceAcceptLanguageControllerDelegate& delegate); |
| ~ReduceAcceptLanguageUtils(); |
| |
| // No copy constructor and no copy assignment operator. |
| ReduceAcceptLanguageUtils(const ReduceAcceptLanguageUtils&) = delete; |
| ReduceAcceptLanguageUtils& operator=(const ReduceAcceptLanguageUtils&) = |
| delete; |
| |
| // Create and return a ReduceAcceptLanguageUtils instance based on provided |
| // `browser_context`. |
| static absl::optional<ReduceAcceptLanguageUtils> Create( |
| BrowserContext* browser_context); |
| |
| // Returns true if `accept_language` matches `content_language` using the |
| // Basic Filtering scheme. See RFC4647 of Section 3.3. |
| static bool DoesAcceptLanguageMatchContentLanguage( |
| const std::string& accept_language, |
| const std::string& content_language); |
| |
| // Starting from each preferred language in `preferred_languages` in order, |
| // return the first matched language if the language matches any language in |
| // `available_languages`, otherwise return absl::nullopt. The matching |
| // algorithm is that if any language in `available_languages` is a wildcard or |
| // matches the language `preferred_languages`, return the matched language as |
| // preferred language. |
| static absl::optional<std::string> GetFirstMatchPreferredLanguage( |
| const std::vector<std::string>& preferred_languages, |
| const std::vector<std::string>& available_languages); |
| |
| // Returns whether reduce accept language can happen for the given URL. |
| // This is true only if the URL is eligible. |
| // |
| // `request_origin` is the origin to be used for reduced accept language |
| // storage. |
| // |
| // TODO(crbug.com/1323776) confirm with CSP sandbox owner if language |
| // preferences need to be hidden from sandboxed origins. |
| static bool OriginCanReduceAcceptLanguage(const url::Origin& request_origin); |
| |
| // Return true if the given `request_origin` opted into the |
| // ReduceAcceptLanguage first-party origin trial. |
| static bool IsReduceAcceptLanguageEnabledForOrigin( |
| const url::Origin& request_origin, |
| const net::HttpResponseHeaders* response_headers); |
| |
| // Updates the accept-language present in headers and returns the reduced |
| // accept language added to accept-language header. This is called when |
| // NavigationRequest was created and when language value changes after |
| // the NavigationRequest was created. |
| // |
| // See `OriginCanReduceAcceptLanguage` for `request_origin`. |
| absl::optional<std::string> AddNavigationRequestAcceptLanguageHeaders( |
| const url::Origin& request_origin, |
| FrameTreeNode* frame_tree_node, |
| net::HttpRequestHeaders* headers); |
| |
| // Reads incoming language and persists it to HostContentSettingsMap prefs |
| // storage as appropriate. Returns a bool indicating whether it needs to |
| // resend the request. |
| bool ReadAndPersistAcceptLanguageForNavigation( |
| const url::Origin& request_origin, |
| const net::HttpRequestHeaders& request_headers, |
| const network::mojom::ParsedHeadersPtr& parsed_headers, |
| bool is_origin_trial_enabled = false); |
| |
| // Looks up which reduced accept language should be used. |
| // |
| // Warning: This could potentially clear the persisted language in pref |
| // storage if the persisted language can't be found in the user's |
| // Accept-Language. |
| // |
| // This is based on the top-level document's origin. |
| // - For main frame navigation, this is the origin of the new document to |
| // commit, given by `request_origin`. |
| // - For iframe navigations, this is the current top-level document's origin |
| // retrieved via `frame_tree_node`. |
| // |
| // See `OriginCanReduceAcceptLanguage` for `request_origin`. |
| absl::optional<std::string> LookupReducedAcceptLanguage( |
| const url::Origin& request_origin, |
| FrameTreeNode* frame_tree_node); |
| |
| // Remove the persisted language for the given top-level document's `origin` |
| // if the corresponding `persisted_language` is not empty and the response |
| // header doesn't have a valid origin trial token when ReduceAcceptLanguage |
| // origin trial is enabled. |
| void RemoveOriginTrialReducedAcceptLanguage( |
| const std::string& persisted_language, |
| const url::Origin& origin, |
| const network::mojom::URLResponseHead* response, |
| FrameTreeNode* frame_tree_node); |
| |
| private: |
| // Captures the state used in applying persist accept language. |
| struct PersistLanguageResult { |
| PersistLanguageResult(); |
| PersistLanguageResult(const PersistLanguageResult& other); |
| ~PersistLanguageResult(); |
| |
| // If true, navigation request needs to resend the requests with the |
| // modified accept language header. |
| bool should_resend_request = false; |
| absl::optional<std::string> language_to_persist = absl::nullopt; |
| }; |
| |
| // Returns whether to persist a language selection based on the given language |
| // information at response time, and also whether the request needs to be |
| // restarted. |
| PersistLanguageResult GetLanguageToPersist( |
| const std::string& initial_accept_language, |
| const std::vector<std::string>& content_languages, |
| const std::vector<std::string>& preferred_languages, |
| const std::vector<std::string>& available_languages, |
| bool is_origin_trial_enabled); |
| |
| // Return the origin to look up the persisted language. |
| // |
| // The reduced accept language should be based on the outermost main |
| // document's origin in most cases. If this call is being made for the |
| // outermost main document, then the NavigationRequest has not yet committed |
| // and we must use the origin from the in-flight NavigationRequest. Subframes |
| // and sub-pages (except Fenced Frames) can use the outermost main document's |
| // last committed origin. Otherwise, it will result in a nullopt return value. |
| // |
| // TODO(https://github.com/WICG/fenced-frame/issues/39) decide whether |
| // Fenced Frames should be treated as an internally-consistent Page, with |
| // language negotiation for the inner main document and/or subframes |
| // that match the main document. |
| absl::optional<url::Origin> GetOriginForLanguageLookup( |
| const url::Origin& request_origin, |
| FrameTreeNode* frame_tree_node); |
| |
| // The delegate is owned by the BrowserContext, which should always outlive |
| // this utility class. |
| raw_ref<ReduceAcceptLanguageControllerDelegate, DanglingUntriaged> delegate_; |
| }; |
| |
| } // namespace content |
| |
| #endif // CONTENT_BROWSER_REDUCE_ACCEPT_LANGUAGE_REDUCE_ACCEPT_LANGUAGE_UTILS_H_ |