| // 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. |
| |
| #include <algorithm> |
| #include <optional> |
| #include <string_view> |
| |
| #include "base/containers/fixed_flat_set.h" |
| #include "base/feature_list.h" |
| #include "base/metrics/histogram_base.h" |
| #include "base/metrics/statistics_recorder.h" |
| #include "base/no_destructor.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/task/thread_pool/thread_pool_instance.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "chrome/browser/content_settings/host_content_settings_map_factory.h" |
| #include "chrome/browser/policy/policy_test_utils.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/content_settings/core/browser/host_content_settings_map.h" |
| #include "components/content_settings/core/common/content_settings.h" |
| #include "components/content_settings/core/common/content_settings_pattern.h" |
| #include "components/content_settings/core/common/content_settings_types.h" |
| #include "components/embedder_support/switches.h" |
| #include "components/language/core/browser/language_prefs.h" |
| #include "components/language/core/browser/pref_names.h" |
| #include "components/language/core/common/language_experiments.h" |
| #include "components/metrics/content/subprocess_metrics_provider.h" |
| #include "components/policy/policy_constants.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/origin_trials_controller_delegate.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/url_loader_interceptor.h" |
| #include "services/network/public/cpp/features.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "url/origin.h" |
| |
| namespace { |
| |
| using ::content::URLLoaderInterceptor; |
| using ::net::test_server::EmbeddedTestServer; |
| |
| enum class FeatureEnableType { FeatureFlagEnable, OriginTrialEnable }; |
| |
| struct ReduceAcceptLanguageTestOptions { |
| std::optional<std::string> content_language_in_parent = std::nullopt; |
| std::optional<std::string> avail_language_in_parent = std::nullopt; |
| std::optional<std::string> vary_in_parent = std::nullopt; |
| std::optional<std::string> content_language_in_child = std::nullopt; |
| std::optional<std::string> avail_language_in_child = std::nullopt; |
| std::optional<std::string> vary_in_child = std::nullopt; |
| bool is_fenced_frame = false; |
| bool is_critical_origin_trial = false; |
| }; |
| |
| struct ServerPortAndValidOriginToken { |
| int port; |
| std::string token; |
| }; |
| |
| const char kLargeLanguages[] = |
| "zh,zh-CN,en-US,en,af,sq,am,ar,an,hy,ast,az,bn,bs,be,eu,br,bg,nl,da,cs,hr," |
| "co,en-AU,en-CA,en-IN,en-NZ,en-ZA,en-GB-oxendict,en-GB,eo,et,fo,fil,fi,fr," |
| "fr-FR,fr-CA,fr-CH,gl,ka,de,gu,gn,el,de-CH,de-LI,de-DE,ht,is,hu,hmn,hi,he," |
| "haw,ig,ja,it-CH,it-IT,it,ga,jv,kn,kk,km,rw,ko,ku,ky,lo,mk,lb,lt,ln,lv,mg," |
| "ms,no,ne,mn,mr,mi,mt,nb,or,oc,ny,nn,pl,fa,ps,om,pt,pt-BR,my,ca,ckb,chr," |
| "ceb,zh-HK,zh-TW,la,ia,id,ha,de-AT,ml,pt-PT,sd,sn,sh,sr,gd,sm,ru,rm,mo,ro," |
| "qu,pa,es-VE,es-UY,es-US,es-ES,es-419,es-MX,es-PE,es-HN,es-CR,es-AR,es,st," |
| "so,sl,sk,si,wa,vi,uz,ug,uk,ur,yi,xh,wo,fy,cy,yo,zu,es-CL,es-CO,su,ta,sv," |
| "sw,tg,tn,to,ti,th,te,tt,tr,tk,tw"; |
| |
| static constexpr const char kFirstPartyOriginUrl[] = "https://127.0.0.1:44444"; |
| static constexpr char kThirdPartyOriginUrl[] = "https://my-site.com:44444"; |
| |
| // Notes: Only use to test origin trial feature with URLLoaderInterceptor. |
| // generate_token.py https://127.0.0.1:44444 DisableReduceAcceptLanguage |
| // --expire-timestamp=2000000000 |
| static constexpr const char kValidFirstPartyToken[] = |
| "A8iumEkw+XVtNR0dFIBuu6jDlDRPOxG4z9lVnq8bunWBNoV//lHIIrHkpQlzZ5Xr9sEW/" |
| "0KZibE/" |
| "Nrt+" |
| "pC3qUQwAAABleyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE6NDQ0NDQiLCAiZmVhdHVyZS" |
| "I6ICJEaXNhYmxlUmVkdWNlQWNjZXB0TGFuZ3VhZ2UiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0" |
| "="; |
| |
| // Notes: Only use to test origin trial feature with URLLoaderInterceptor. |
| // generate_token.py https://my-site.com:44444 DisableReduceAcceptLanguage |
| // --is-third-party --expire-timestamp=2000000000 |
| static constexpr const char kValidThirdPartyToken[] = |
| "A8zLsSI/JcHxa+c6CvX2asTG2Uh62FUsb9jTZVszTHGyert8A22L/" |
| "XgCdGVFjujmRtDHeAd7ctVgUr7IWWgVgAwAAAB9eyJvcmlnaW4iOiAiaHR0cHM6Ly9teS1zaXR" |
| "lLmNvbTo0NDQ0NCIsICJmZWF0dXJlIjogIkRpc2FibGVSZWR1Y2VBY2NlcHRMYW5ndWFnZSIsI" |
| "CJleHBpcnkiOiAyMDAwMDAwMDAwLCAiaXNUaGlyZFBhcnR5IjogdHJ1ZX0="; |
| |
| // Notes: Only use to test origin trial feature with URLLoaderInterceptor. |
| // generate_token.py https://my-site.com:44444 DisableReduceAcceptLanguage |
| // --expire-timestamp=2000000000 |
| static constexpr const char kValidMySiteFirstPartyToken[] = |
| "A5z0r3ggtGZbmJt+zZWHzeLJeeXdkzmi38nNssSJet5TbRS+" |
| "gdKQy9f8b5YCJvK478XVHd6fCKXOSHgxNQTV2ggAAABneyJvcmlnaW4iOiAiaHR0cHM6Ly9teS" |
| "1zaXRlLmNvbTo0NDQ0NCIsICJmZWF0dXJlIjogIkRpc2FibGVSZWR1Y2VBY2NlcHRMYW5ndWFn" |
| "ZSIsICJleHBpcnkiOiAyMDAwMDAwMDAwfQ=="; |
| |
| static constexpr const char kInvalidOriginToken[] = |
| "AjfC47H1q8/Ho5ALFkjkwf9CBK6oUUeRTlFc50Dj+eZEyGGKFIY2WTxMBfy8cLc3" |
| "E0nmFroDA3OmABmO5jMCFgkAAABXeyJvcmlnaW4iOiAiaHR0cDovL3ZhbGlkLmV4" |
| "YW1wbGUuY29tOjgwIiwgImZlYXR1cmUiOiAiRnJvYnVsYXRlIiwgImV4cGlyeSI6" |
| "IDIwMDAwMDAwMDB9"; |
| |
| static constexpr const char kDeprecationTrialName[] = |
| "DisableReduceAcceptLanguage"; |
| |
| } // namespace |
| |
| class ReduceAcceptLanguageBrowserTest : public policy::PolicyTest { |
| public: |
| ReduceAcceptLanguageBrowserTest() = default; |
| |
| void SetUp() override { |
| EnabledFeatures(); |
| InProcessBrowserTest::SetUp(); |
| } |
| |
| void SetUpOnMainThread() override { |
| // We use a URLLoaderInterceptor, we also can use the EmbeddedTestServer. |
| url_loader_interceptor_ = std::make_unique<URLLoaderInterceptor>( |
| base::BindRepeating(&ReduceAcceptLanguageBrowserTest::InterceptRequest, |
| base::Unretained(this))); |
| |
| InProcessBrowserTest::SetUpOnMainThread(); |
| } |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| // The public key for the default private key used by the |
| // tools/origin_trials/generate_token.py tool. |
| static constexpr char kOriginTrialTestPublicKey[] = |
| "dRCs+TocuKkocNKa0AtZ4awrt9XKH2SQCI6o4FY6BNA="; |
| command_line->AppendSwitchASCII(embedder_support::kOriginTrialPublicKey, |
| kOriginTrialTestPublicKey); |
| } |
| |
| void TearDownOnMainThread() override { |
| // Clean up any saved settings after test run. |
| browser() |
| ->profile() |
| ->GetOriginTrialsControllerDelegate() |
| ->ClearPersistedTokens(); |
| |
| url_loader_interceptor_.reset(); |
| intercepted_load_urls_.clear(); |
| InProcessBrowserTest::TearDownOnMainThread(); |
| } |
| |
| void SetTestOptions(const ReduceAcceptLanguageTestOptions& test_options, |
| const std::set<GURL>& expected_request_urls) { |
| test_options_ = test_options; |
| expected_request_urls_ = expected_request_urls; |
| } |
| |
| GURL CreateServiceWorkerRequestUrl() const { |
| return GURL( |
| base::StrCat({kFirstPartyOriginUrl, "/create_service_worker.html"})); |
| } |
| |
| GURL NavigationPreloadWorkerRequestUrl() const { |
| return GURL( |
| base::StrCat({kFirstPartyOriginUrl, "/navigation_preload_worker.js"})); |
| } |
| |
| GURL SameOriginRequestUrl() const { |
| return GURL( |
| base::StrCat({kFirstPartyOriginUrl, "/same_origin_request.html"})); |
| } |
| |
| GURL SameOriginIframeUrl() const { |
| return GURL( |
| base::StrCat({kFirstPartyOriginUrl, "/same_origin_iframe.html"})); |
| } |
| |
| GURL SameOriginImgUrl() const { |
| return GURL(base::StrCat({kFirstPartyOriginUrl, "/same_origin_img.html"})); |
| } |
| |
| GURL SimpleImgUrl() const { |
| return GURL( |
| base::StrCat({kFirstPartyOriginUrl, "/subresource_simple.jpg"})); |
| } |
| |
| GURL SimpleRequestUrl() const { |
| return GURL(base::StrCat({kFirstPartyOriginUrl, "/subframe_simple.html"})); |
| } |
| |
| GURL LastRequestUrl() const { |
| return url_loader_interceptor_->GetLastRequestURL(); |
| } |
| |
| GURL CrossOriginSubresourceUrl() const { |
| return GURL( |
| base::StrCat({kFirstPartyOriginUrl, "/cross_origin_subresource.html"})); |
| } |
| |
| GURL CrossOriginMetaTagInjectingJavascriptUrl() const { |
| return GURL(base::StrCat({kThirdPartyOriginUrl, "/meta.js"})); |
| } |
| |
| GURL CrossOriginCssRequestUrl() const { |
| return GURL(base::StrCat( |
| {kThirdPartyOriginUrl, "/subresource_redirect_style.css"})); |
| } |
| |
| GURL CrossOriginSimpleImgUrl() const { |
| return GURL( |
| base::StrCat({kThirdPartyOriginUrl, "/subresource_simple.jpg"})); |
| } |
| |
| // Navigate `url` and wait for NavigateToURL to complete, including all |
| // subframes and verify whether the Accept-Language header value of last |
| // request in `expected_request_urls_` is `expect_accept_language`. |
| void NavigateAndVerifyAcceptLanguageOfLastRequest( |
| const GURL& url, |
| const std::optional<std::string>& expect_accept_language) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| const std::optional<std::string>& accept_language_header_value = |
| GetLastAcceptLanguageHeaderValue(); |
| if (!expect_accept_language) { |
| EXPECT_FALSE(accept_language_header_value.has_value()); |
| } else { |
| EXPECT_TRUE(accept_language_header_value.has_value()); |
| EXPECT_EQ(expect_accept_language.value(), |
| accept_language_header_value.value()); |
| } |
| } |
| |
| void VerifyNavigatorLanguages( |
| const std::vector<std::string>& expect_languages) { |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| base::Value::List languages_list = |
| content::EvalJs(web_contents, "navigator.languages") |
| .TakeValue() |
| .TakeList(); |
| std::vector<std::string> actual_languages; |
| for (const auto& result : languages_list) { |
| actual_languages.push_back(result.GetString()); |
| } |
| |
| EXPECT_EQ(expect_languages, actual_languages); |
| } |
| |
| void SetPrefsAcceptLanguage( |
| const std::vector<std::string>& accept_languages) { |
| auto language_prefs = std::make_unique<language::LanguagePrefs>( |
| browser()->profile()->GetPrefs()); |
| language_prefs->SetUserSelectedLanguagesList(accept_languages); |
| } |
| |
| // Mock the site set content-language behavior. If site supports the language |
| // in the accept-language request header, set the content-language the same as |
| // accept-language, otherwise set as the first available language. |
| std::string GetResponseContentLanguage( |
| const std::string& accept_language, |
| const std::vector<std::string>& avail_languages) { |
| auto iter = std::ranges::find(avail_languages, accept_language); |
| return iter != avail_languages.end() ? *iter : avail_languages[0]; |
| } |
| |
| protected: |
| // Return the feature list for the tests. |
| virtual void EnabledFeatures() = 0; |
| base::test::ScopedFeatureList scoped_feature_list_; |
| |
| // Returns whether a given |header| has been received for the last request. |
| bool HasReceivedHeader(const std::string& header) const { |
| return url_loader_interceptor_->GetLastRequestHeaders().HasHeader(header); |
| } |
| |
| void ResetURLAndAcceptLanguageSequence() { |
| actual_url_accept_language_.clear(); |
| } |
| |
| void VerifyURLAndAcceptLanguageSequence( |
| const std::vector<std::vector<std::string>>& expect_url_accept_language, |
| const std::string& message = "") { |
| EXPECT_EQ(actual_url_accept_language_, expect_url_accept_language) |
| << message; |
| } |
| |
| // As origin trial needs to start a service in a specific port instead of |
| // random port, sometime the specific port is not ready, this can cause tests |
| // are flaky. Allow test server to retry on provided ports and set the origin |
| // trial token if server starts succeed. |
| void StartTestServerAndSetToken( |
| net::EmbeddedTestServer* http_server, |
| std::vector<ServerPortAndValidOriginToken> port_tokens, |
| bool third_party_origin = false) { |
| // Try start server in random ports. |
| if (port_tokens.empty()) { |
| EXPECT_TRUE(http_server->Start()); |
| return; |
| } |
| |
| // Try different ports and assign the origin token. |
| bool started = false; |
| for (size_t i = 0; i < port_tokens.size(); i++) { |
| LOG(INFO) << "Start server on port " << port_tokens[i].port |
| << " in attempt " << i << "."; |
| started = http_server->Start(port_tokens[i].port); |
| |
| if (started) { |
| third_party_origin ? SetValidThirdPartyToken(port_tokens[i].token) |
| : SetValidFirstPartyToken(port_tokens[i].token); |
| break; |
| } |
| } |
| EXPECT_TRUE(started); |
| } |
| |
| void SetValidFirstPartyToken(const std::string& token) { |
| valid_first_party_token_ = token; |
| } |
| |
| void SetValidThirdPartyToken(const std::string& token) { |
| valid_third_party_token_ = token; |
| } |
| |
| void SetOriginTrialFirstPartyToken(const std::string& token) { |
| origin_trial_first_party_token_ = token; |
| } |
| |
| void SetOriginTrialThirdPartyToken(const std::string& token) { |
| origin_trial_third_party_token_ = token; |
| } |
| |
| std::string GetFirstLanguage(std::string_view language_list) { |
| auto end = language_list.find(","); |
| return std::string(language_list.substr(0, end)); |
| } |
| |
| std::vector<std::vector<std::string>> actual_url_accept_language_; |
| std::string origin_trial_first_party_token_; |
| std::string origin_trial_third_party_token_; |
| std::string valid_first_party_token_; |
| std::string valid_third_party_token_; |
| std::set<GURL> intercepted_load_urls_; |
| |
| private: |
| // Returns the value of the Accept-Language request header from the last sent |
| // request, or nullopt if the header could not be read. |
| const std::optional<std::string>& GetLastAcceptLanguageHeaderValue() { |
| last_accept_language_value_ = |
| url_loader_interceptor_->GetLastRequestHeaders().GetHeader( |
| "accept-language"); |
| return last_accept_language_value_; |
| } |
| |
| // URLLoaderInterceptor callback |
| bool InterceptRequest(URLLoaderInterceptor::RequestParams* params) { |
| if (expected_request_urls_.find(params->url_request.url) == |
| expected_request_urls_.end()) |
| return false; |
| |
| intercepted_load_urls_.insert(params->url_request.url); |
| |
| if (params->url_request.url == CrossOriginSubresourceUrl()) { |
| return RespondForCrossOriginSubResourceOriginTrialUrl(params); |
| } |
| if (params->url_request.url == CrossOriginMetaTagInjectingJavascriptUrl()) { |
| return RespondCrossOriginMetaTagInjectingScriptUrl(params); |
| } |
| |
| std::string headers = "HTTP/1.1 200 OK\r\n"; |
| if (params->url_request.url == NavigationPreloadWorkerRequestUrl()) { |
| base::StrAppend(&headers, {"Content-Type: text/javascript\r\n"}); |
| } else { |
| base::StrAppend(&headers, {"Content-Type: text/html\r\n"}); |
| } |
| |
| if (test_options_.is_fenced_frame) { |
| base::StrAppend(&headers, {"Supports-Loading-Mode: fenced-frame\r\n"}); |
| } |
| static constexpr auto kSubresourcePaths = |
| base::MakeFixedFlatSet<std::string_view>({ |
| "/subframe_iframe_basic.html", |
| "/subframe_iframe_3p.html", |
| "/subframe_redirect.html", |
| "/subframe_simple.html", |
| "/subframe_simple_3p.html", |
| "/subresource_simple.jpg", |
| "/subresource_redirect_style.css", |
| }); |
| const std::string path = params->url_request.url.path(); |
| if (base::Contains(kSubresourcePaths, path)) { |
| base::StrAppend(&headers, {BuildSubresourceResponseHeader()}); |
| } else { |
| base::StrAppend(&headers, {BuildResponseHeader()}); |
| } |
| |
| // Build mock header for the first party origin if the token is not empty. |
| if (!origin_trial_first_party_token_.empty()) { |
| base::StrAppend( |
| &headers, |
| {"Origin-Trial: ", origin_trial_first_party_token_, "\r\n"}); |
| if (test_options_.is_critical_origin_trial) { |
| base::StrAppend( |
| &headers, |
| {"Critical-Origin-Trial: ", kDeprecationTrialName, "\r\n"}); |
| } |
| } |
| |
| // Only build mock header with third party origin trial tokens for the third |
| // party requests. |
| const GURL origin = params->url_request.url.DeprecatedGetOriginAsURL(); |
| if (!origin_trial_third_party_token_.empty() && |
| origin == GURL(kThirdPartyOriginUrl)) { |
| base::StrAppend( |
| &headers, |
| {"Origin-Trial: ", origin_trial_third_party_token_, "\r\n"}); |
| if (test_options_.is_critical_origin_trial) { |
| base::StrAppend( |
| &headers, |
| {"Critical-Origin-Trial: ", kDeprecationTrialName, "\r\n"}); |
| } |
| } |
| |
| static constexpr auto kServiceWorkerPaths = |
| base::MakeFixedFlatSet<std::string_view>({ |
| "/create_service_worker.html", |
| "/navigation_preload_worker.js", |
| }); |
| |
| std::string resource_path; |
| if (base::Contains(kServiceWorkerPaths, path)) { |
| resource_path = "chrome/test/data/service_worker"; |
| } else { |
| resource_path = "chrome/test/data/reduce_accept_language"; |
| } |
| resource_path.append( |
| static_cast<std::string>(params->url_request.url.path_piece())); |
| |
| URLLoaderInterceptor::WriteResponse(resource_path, params->client.get(), |
| &headers, std::nullopt, |
| /*url=*/params->url_request.url); |
| return true; |
| } |
| |
| std::string BuildResponseHeader() { |
| std::string headers; |
| if (test_options_.content_language_in_parent) { |
| base::StrAppend( |
| &headers, {"Content-Language: ", |
| test_options_.content_language_in_parent.value(), "\r\n"}); |
| } |
| if (test_options_.avail_language_in_parent) { |
| base::StrAppend(&headers, |
| {"Avail-Language: ", |
| test_options_.avail_language_in_parent.value(), "\r\n"}); |
| } |
| if (test_options_.vary_in_parent) { |
| base::StrAppend(&headers, |
| {"Vary: ", test_options_.vary_in_parent.value(), "\n"}); |
| } |
| return headers; |
| } |
| |
| std::string BuildSubresourceResponseHeader() { |
| std::string headers; |
| if (test_options_.content_language_in_child) { |
| base::StrAppend( |
| &headers, {"Content-Language: ", |
| test_options_.content_language_in_child.value(), "\r\n"}); |
| } |
| if (test_options_.avail_language_in_child) { |
| base::StrAppend(&headers, |
| {"Avail-Language: ", |
| test_options_.avail_language_in_child.value(), "\r\n"}); |
| } |
| if (test_options_.vary_in_child) { |
| base::StrAppend(&headers, |
| {"Vary: ", test_options_.vary_in_child.value(), "\r\n"}); |
| } |
| return headers; |
| } |
| |
| bool RespondForCrossOriginSubResourceOriginTrialUrl( |
| URLLoaderInterceptor::RequestParams* params) { |
| if (origin_trial_third_party_token_.empty()) { |
| return false; |
| } |
| // Construct the origin trial header response. |
| std::string headers = "HTTP/1.1 200 OK\nContent-Type: text/html\n"; |
| std::string body = base::StrCat( |
| {"<html><head><script src=\"", |
| CrossOriginMetaTagInjectingJavascriptUrl().spec(), "\"></script>", |
| "<link rel=\"stylesheet\" href=\"", CrossOriginCssRequestUrl().spec(), |
| "\">", "</head> <body> <img src=\"", CrossOriginSimpleImgUrl().spec(), |
| "\"></img> This page has no title.</body></html>"}); |
| URLLoaderInterceptor::WriteResponse(headers, body, params->client.get()); |
| return true; |
| } |
| |
| bool RespondCrossOriginMetaTagInjectingScriptUrl( |
| URLLoaderInterceptor::RequestParams* params) { |
| if (origin_trial_third_party_token_.empty()) { |
| return false; |
| } |
| // Construct the origin trial header response. |
| std::string headers = |
| "HTTP/1.1 200 OK\nContent-Type: application/javascript\n"; |
| std::string body = |
| base::StrCat({"const otMeta = document.createElement('meta'); " |
| "otMeta.httpEquiv = 'origin-trial'; " |
| "otMeta.content = '", |
| origin_trial_third_party_token_, |
| "'; " |
| "document.head.append(otMeta); "}); |
| URLLoaderInterceptor::WriteResponse(headers, body, params->client.get()); |
| return true; |
| } |
| |
| std::unique_ptr<URLLoaderInterceptor> url_loader_interceptor_; |
| std::set<GURL> expected_request_urls_; |
| ReduceAcceptLanguageTestOptions test_options_; |
| std::optional<std::string> last_accept_language_value_; |
| }; |
| |
| // Browser tests that consider ReduceAcceptLanguage feature disabled. |
| class DisableFeatureReduceAcceptLanguageBrowserTest |
| : public ReduceAcceptLanguageBrowserTest { |
| void EnabledFeatures() override { |
| scoped_feature_list_.InitWithFeatures( |
| {}, {network::features::kReduceAcceptLanguage, |
| network::features::kReduceAcceptLanguageHTTP}); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(DisableFeatureReduceAcceptLanguageBrowserTest, |
| NoAcceptLanguageHeader) { |
| SetTestOptions({.content_language_in_parent = "en", |
| .avail_language_in_parent = "en, en-US", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // Expect no Accept-Language header added because browser_tests can only check |
| // headers in navigation layer, browser_tests can't see headers added by |
| // network stack. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), |
| std::nullopt); |
| VerifyNavigatorLanguages({"zh", "en-US"}); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DisableFeatureReduceAcceptLanguageBrowserTest, |
| IframeNoAcceptLanguageHeader) { |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "es", |
| .avail_language_in_child = "es, en-US", |
| .vary_in_child = "accept-language"}, |
| {SameOriginIframeUrl(), SimpleRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // Expect no Accept-Language header added because browser_tests can only check |
| // headers in navigation layer, browser_tests can't see headers added by |
| // network stack. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginIframeUrl(), |
| std::nullopt); |
| EXPECT_EQ(LastRequestUrl().path(), "/subframe_simple.html"); |
| } |
| |
| // Browser tests that using Enterprise policy to control ReduceAcceptLanguage |
| // feature. |
| class ReduceAcceptLanguageEnterprisePolicyBrowserTest |
| : public ReduceAcceptLanguageBrowserTest, |
| public ::testing::WithParamInterface<policy::PolicyTest::BooleanPolicy> { |
| public: |
| static std::string DescribeParams( |
| const ::testing::TestParamInfo<ParamType>& info) { |
| switch (info.param) { |
| case policy::PolicyTest::BooleanPolicy::kNotConfigured: |
| return "NotConfigured"; |
| case policy::PolicyTest::BooleanPolicy::kTrue: |
| return "True"; |
| case policy::PolicyTest::BooleanPolicy::kFalse: |
| return "False"; |
| } |
| } |
| |
| void SetUpInProcessBrowserTestFixture() override { |
| policy::PolicyTest::SetUpInProcessBrowserTestFixture(); |
| |
| if (GetParam() == policy::PolicyTest::BooleanPolicy::kNotConfigured) { |
| return; |
| } |
| |
| policy::PolicyMap policies; |
| SetPolicy( |
| &policies, policy::key::kReduceAcceptLanguageEnabled, |
| base::Value(GetParam() == policy::PolicyTest::BooleanPolicy::kTrue)); |
| UpdateProviderPolicy(policies); |
| } |
| |
| void EnabledFeatures() override { |
| scoped_feature_list_.InitWithFeatures( |
| {network::features::kReduceAcceptLanguage}, {}); |
| } |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| ReduceAcceptLanguageEnterprisePolicyBrowserTest, |
| ::testing::Values(policy::PolicyTest::BooleanPolicy::kNotConfigured, |
| policy::PolicyTest::BooleanPolicy::kFalse, |
| policy::PolicyTest::BooleanPolicy::kTrue), |
| &ReduceAcceptLanguageEnterprisePolicyBrowserTest::DescribeParams); |
| |
| IN_PROC_BROWSER_TEST_P(ReduceAcceptLanguageEnterprisePolicyBrowserTest, |
| PolicyIsFollowed) { |
| SetTestOptions({.content_language_in_parent = "en", |
| .avail_language_in_parent = "en, en-US", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // Both true and the default (no parameter) should be enabled. |
| const bool expect_feature_disabled = |
| GetParam() == policy::PolicyTest::BooleanPolicy::kFalse; |
| if (expect_feature_disabled) { |
| // Expect no Accept-Language header added because browser_tests can only |
| // check headers in navigation layer, browser_tests can't see headers added |
| // by network stack. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), |
| std::nullopt); |
| VerifyNavigatorLanguages({"zh", "en-US"}); |
| } else { |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), |
| "en-US,en;q=0.9"); |
| VerifyNavigatorLanguages({"zh"}); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_P(ReduceAcceptLanguageEnterprisePolicyBrowserTest, |
| PolicyIsFollowedIframe) { |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "es", |
| .avail_language_in_child = "es, en-US", |
| .vary_in_child = "accept-language"}, |
| {SameOriginIframeUrl(), SimpleRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // Both true and the default (no parameter) should be enabled. |
| const bool expect_feature_disabled = |
| GetParam() == policy::PolicyTest::BooleanPolicy::kFalse; |
| if (expect_feature_disabled) { |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginIframeUrl(), |
| std::nullopt); |
| VerifyNavigatorLanguages({"zh", "en-US"}); |
| } else { |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginIframeUrl(), |
| "en-US,en;q=0.9"); |
| VerifyNavigatorLanguages({"zh"}); |
| } |
| EXPECT_EQ(LastRequestUrl().path(), "/subframe_simple.html"); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(ReduceAcceptLanguageEnterprisePolicyBrowserTest, |
| PolicyIsFollowedImgSubresource) { |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "es", |
| .avail_language_in_child = "es, en-US", |
| .vary_in_child = "accept-language"}, |
| {SameOriginImgUrl(), SimpleImgUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // Both true and the default (no parameter) should be enabled. |
| const bool expect_feature_disabled = |
| GetParam() == policy::PolicyTest::BooleanPolicy::kFalse; |
| if (expect_feature_disabled) { |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SimpleImgUrl(), std::nullopt); |
| VerifyNavigatorLanguages({"zh", "en-US"}); |
| } else { |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SimpleImgUrl(), |
| "en-US,en;q=0.9"); |
| VerifyNavigatorLanguages({"zh"}); |
| } |
| EXPECT_EQ(LastRequestUrl().path(), "/subresource_simple.jpg"); |
| } |
| |
| // Tests same origin requests with the ReduceAcceptLanguage feature enabled. |
| class SameOriginReduceAcceptLanguageBrowserTest |
| : public ReduceAcceptLanguageBrowserTest, |
| public testing::WithParamInterface<bool> { |
| protected: |
| void EnabledFeatures() override { |
| // True: Enable the general feature for Reduce Accept-Language. |
| // False: Only enable reduction for HTTP header. |
| if (GetParam()) { |
| scoped_feature_list_.InitWithFeatures( |
| {network::features::kReduceAcceptLanguage}, {}); |
| } else { |
| scoped_feature_list_.InitWithFeatures( |
| {network::features::kReduceAcceptLanguageHTTP}, |
| {network::features::kReduceAcceptLanguage}); |
| } |
| } |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| SameOriginReduceAcceptLanguageBrowserTest, |
| testing::Bool()); |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| LargeLanguageListAndScriptDisable) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| |
| SetPrefsAcceptLanguage(base::SplitString( |
| kLargeLanguages, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)); |
| // Expect accept-language set as the negotiation language. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), |
| "en-US,en;q=0.9"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // same_origin_request_url request has two fetch Prefs requests: one fetch |
| // for initially adding header and another one for restart fetch. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 2); |
| // One store for same_origin_request_url main frame. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 1); |
| |
| // Disable script for first party origin. |
| HostContentSettingsMapFactory::GetForProfile(browser()->profile()) |
| ->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromURL(GURL(kFirstPartyOriginUrl)), |
| ContentSettingsPattern::Wildcard(), ContentSettingsType::JAVASCRIPT, |
| CONTENT_SETTING_BLOCK); |
| |
| // Even Script disabled, it still expects reduced accept-language. The second |
| // navigation should use the language after negotiation which is en-US. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), |
| "en-US,en;q=0.9"); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| EmptyUserAcceptLanguage) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({}); |
| // Expect no reduced Accept-Language header set on navigation request. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), |
| std::nullopt); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguagePrefValueIsEmpty", true, 1); |
| |
| // No prefs read and write operations. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 0); |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 0); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| NoAvailLanguageHeader) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = std::nullopt, |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en"}); |
| // Expect accept-language set as the first user's accept-language |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), "zh"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 1); |
| // Persist won't happen. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 0); |
| |
| // Verify that navigator.languages only returns an array of length 1 if |
| // ReduceAcceptLanguage is enabled. For the HTTP-only feature, it should |
| // be no change and return the full list of languages. |
| if (GetParam()) { |
| VerifyNavigatorLanguages({"zh"}); |
| } else { |
| VerifyNavigatorLanguages({"zh", "en"}); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| NoContentLanguageHeader) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = std::nullopt, |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en"}); |
| // Expect accept-language set as the first user's accept-language |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), "zh"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure metrics report correctly. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kAvailLanguageAndContentLanguageHeaderPresent=*/2, 0); |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 1); |
| // Persist won't happen. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 0); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| EmptyAvailLanguageAcceptLanguages) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en"}); |
| // Expect accept-language set as the first user's accept-language |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), "zh"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // One request, one prefs fetch when initially adding header. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 1); |
| // Persist won't happen. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 0); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| AvailLanguageAcceptLanguagesWhiteSpace) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = " ", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en"}); |
| // Expect accept-language set as the first user's accept-language |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), "zh"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure no restart happens. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 0); |
| // One request, one Prefs fetch request when initially adding header. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 1); |
| // Persist won't happen. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 0); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| SiteLanguageMatchNonPrimaryLanguage) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // Expect accept-language set as negotiated language: en-US. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), |
| "en-US,en;q=0.9"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure only restart once. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 1); |
| // One request same_origin_request_url: one Prefs fetch request when initial |
| // add header. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 2); |
| // One store for same_origin_request_url main frame. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 1); |
| |
| base::HistogramTester histograms_after; |
| SetTestOptions({.content_language_in_parent = "en-US", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| |
| // The second request should send out with the first matched negotiation |
| // language en-US instead of ja. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), |
| "en-US,en;q=0.9"); |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure no restart happen. |
| histograms_after.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 0); |
| // One request same_origin_request_url: one fetch for initially adding header |
| // and no restart fetch. |
| histograms_after.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 1); |
| // One store for same_origin_request_url main frame. |
| histograms_after.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 1); |
| } |
| |
| // Verify no endless resend requests for the service worker navigation preload |
| // requests. |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| ServiceWorkerNavigationPreload) { |
| SetTestOptions( |
| {.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language"}, |
| {CreateServiceWorkerRequestUrl(), NavigationPreloadWorkerRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| base::HistogramTester histograms; |
| // Expect accept-language set as negotiated language: en-US. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(CreateServiceWorkerRequestUrl(), |
| "en-US,en;q=0.9"); |
| // Register a service worker that uses navigation preload. |
| EXPECT_EQ("DONE", EvalJs(browser()->tab_strip_model()->GetActiveWebContents(), |
| "register('/navigation_preload_worker.js', '/');")); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Total two Prefs fetch requests: one for initially adding header and another |
| // one for the restart request adding header. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 2); |
| // One store for create_service_worker_request_url main frame. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 1); |
| |
| // Verify "Service-Worker-Navigation-Preload" is present and no future resend |
| // requests when site responses with expected content-language 'en-US'. |
| base::HistogramTester histograms2; |
| SetTestOptions( |
| {.content_language_in_parent = "en-US", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language"}, |
| {CreateServiceWorkerRequestUrl(), NavigationPreloadWorkerRequestUrl()}); |
| |
| NavigateAndVerifyAcceptLanguageOfLastRequest(CreateServiceWorkerRequestUrl(), |
| "en-US,en;q=0.9"); |
| EXPECT_TRUE(HasReceivedHeader("Service-Worker-Navigation-Preload")); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // One Prefs fetch request when initially adding header. No restart. |
| histograms2.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 1); |
| histograms2.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kServiceWorkerPreloadRequest=*/2, 1); |
| // Ensure no restart happen. |
| histograms2.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 0); |
| histograms2.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 0); |
| |
| // Verify "Service-Worker-Navigation-Preload" is present and no future resend |
| // requests even when site made mistake responding with unexpected |
| // content-language 'es'. |
| base::HistogramTester histograms3; |
| SetTestOptions( |
| {.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language"}, |
| {CreateServiceWorkerRequestUrl(), NavigationPreloadWorkerRequestUrl()}); |
| |
| NavigateAndVerifyAcceptLanguageOfLastRequest(CreateServiceWorkerRequestUrl(), |
| "en-US,en;q=0.9"); |
| EXPECT_TRUE(HasReceivedHeader("Service-Worker-Navigation-Preload")); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // One Prefs fetch request when initially adding header. |
| histograms3.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 1); |
| histograms3.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kServiceWorkerPreloadRequest=*/2, 1); |
| // Ensure no restart happen. |
| histograms3.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 0); |
| histograms3.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 0); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| SiteLanguageMatchPrimaryLanguage) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"es", "en-US"}); |
| |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), "es"); |
| // Ensure no restart happen. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 0); |
| |
| // The second request should send out with the same preferred language. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), "es"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // For above two same_origin_request_url requests, both only have one Prefs |
| // fetch when initially adding header. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 2); |
| // Expect no perf storage updates. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 0); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| SubresourceRequestNoRestart) { |
| base::HistogramTester histograms; |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginImgUrl(), SimpleImgUrl()}); |
| SetPrefsAcceptLanguage({"es", "en-US"}); |
| |
| // Initial request. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginImgUrl(), "es"); |
| EXPECT_EQ(LastRequestUrl().path(), "/subresource_simple.jpg"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure no restart happens. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 0); |
| // Total two different url requests: |
| // * same_origin_img.html: one fetch for initially adding header. |
| // * subresource_simple.jpg: no prefs read, it directly reads from the |
| // navigation commit language. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| SiteLanguageMatchMultipleLanguage) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US, ja", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en-US", "ja"}); |
| |
| // Expect accept-language set as negotiated language: en-US. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), |
| "en-US,en;q=0.9"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure only restart once. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 1); |
| // One request same_origin_request_url: one fetch for initially adding header |
| // and another one for restart fetch. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 2); |
| // One store for same_origin_request_url main frame. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 1); |
| |
| base::HistogramTester histograms_after; |
| SetTestOptions({.content_language_in_parent = "en-US", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| |
| // The second request should send out with the first matched negotiation |
| // language en-US instead of ja. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), |
| "en-US,en;q=0.9"); |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure no restart happen. |
| histograms_after.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 0); |
| // One request same_origin_request_url: one fetch for initially adding header |
| // and no restart fetch. |
| histograms_after.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 1); |
| // One store for same_origin_request_url main frame. |
| histograms_after.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| SiteLanguageDontMatchAnyPreferredLanguage) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "ja"}); |
| |
| // Expect accept-language set as the first user's accept-language. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), "zh"); |
| // Ensure no restart happen. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 0); |
| |
| // The second request should send out with the same first preferred language. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), "zh"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // For above two same_origin_request_url requests: each has one Prefs fetch |
| // request when initially adding header. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 2); |
| // Expect no perf storage updates. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 0); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| PersistedAcceptLanguageNotAvailable) { |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, ja, en-US", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "ja", "en-US"}); |
| // The first request should send out with the negotiated language which is ja. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), "ja"); |
| |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| // The second request should send out with the new negotiated language en-US. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), |
| "en-US,en;q=0.9"); |
| |
| base::HistogramTester histograms; |
| SetPrefsAcceptLanguage({"zh"}); |
| // The third request should send out with the first accept-language since the |
| // persisted language not available in latest user's accept-language list. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), "zh"); |
| // The previous persisted language `en-US` is not in the user's preference |
| // list. Verify that a language clear operation occurred. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.ClearLatency", 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| IframeReduceAcceptLanguage) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "es", |
| .avail_language_in_child = "es, en-US", |
| .vary_in_child = "accept-language"}, |
| {SameOriginIframeUrl(), SimpleRequestUrl()}); |
| |
| SetPrefsAcceptLanguage(base::SplitString( |
| kLargeLanguages, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)); |
| |
| // Iframe request expect to be the language after language negotiation. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginIframeUrl(), |
| "en-US,en;q=0.9"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure restart happen once. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 1); |
| |
| // Total two different url requests: |
| // * same_origin_iframe_url: one fetch for initially adding header and another |
| // one for the restart request adding header. |
| // * simple_request_url: one fetch for initially adding header. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 3); |
| |
| EXPECT_EQ(LastRequestUrl().path(), "/subframe_simple.html"); |
| |
| // Disable script for first party origin. |
| HostContentSettingsMapFactory::GetForProfile(browser()->profile()) |
| ->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromURL(GURL(kFirstPartyOriginUrl)), |
| ContentSettingsPattern::Wildcard(), ContentSettingsType::JAVASCRIPT, |
| CONTENT_SETTING_BLOCK); |
| |
| // Even Script disabled, it still expects reduced accept-language. The second |
| // navigation should use the language after negotiation which is en-US. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginIframeUrl(), |
| "en-US,en;q=0.9"); |
| EXPECT_EQ(LastRequestUrl().path(), "/subframe_simple.html"); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| ImgSubresourceReduceAcceptLanguage) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "es", |
| .avail_language_in_child = "es, en-US", |
| .vary_in_child = "accept-language"}, |
| {SameOriginImgUrl(), SimpleImgUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // Subresource img request expect to be the language after language |
| // negotiation. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginImgUrl(), |
| "en-US,en;q=0.9"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure restart happen once. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 1); |
| // Total two different URL requests, only same_origin_img_url request has two |
| // fetch Prefs requests: one fetch for initially adding header and another one |
| // for the restart request adding header. For image request, it will directly |
| // read the persisted from the navigation commit reduced accept language. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 2); |
| // One store for same_origin_img_url main frame. |
| |
| EXPECT_EQ(LastRequestUrl().path(), "/subresource_simple.jpg"); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| IframeNoContentLanguageInChild) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = std::nullopt, |
| .avail_language_in_child = "es, en-US", |
| .vary_in_child = "accept-language"}, |
| {SameOriginIframeUrl(), SimpleRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // Iframe request expect to be the language after language negotiation. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginIframeUrl(), |
| "en-US,en;q=0.9"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure restart happen once. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 1); |
| // Total two different URL requests: |
| // * same_origin_iframe_url: one fetch for initially adding header and another |
| // one for the restart request adding header. |
| // * simple_request_url: one fetch for initially adding header. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 3); |
| // One store for same_origin_iframe_url main frame. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 1); |
| |
| EXPECT_EQ(LastRequestUrl().path(), "/subframe_simple.html"); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| IframeNoAvailLanguageAcceptLanguageInChild) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "es", |
| .avail_language_in_child = std::nullopt, |
| .vary_in_child = "accept-language"}, |
| {SameOriginIframeUrl(), SimpleRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // Iframe request expect to be the language after language negotiation. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginIframeUrl(), |
| "en-US,en;q=0.9"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure restart happen once. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 1); |
| // Total two different URL requests: |
| // * same_origin_iframe_url: one fetch for initially adding header and another |
| // one for the restart request adding header. |
| // * simple_request_url: one fetch for initially adding header. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 3); |
| // One store for same_origin_iframe_url main frame. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 1); |
| |
| EXPECT_EQ(LastRequestUrl().path(), "/subframe_simple.html"); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| IframeSameContentLanguage) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "es", |
| .avail_language_in_child = "es, en-US", |
| .vary_in_child = "accept-language"}, |
| {SameOriginIframeUrl(), SimpleRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // Iframe request expect to be the language after language negotiation. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginIframeUrl(), |
| "en-US,en;q=0.9"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure restart happen once. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 1); |
| // Total two different URL requests: |
| // * same_origin_iframe_url: one fetch for initially adding header and another |
| // one for the restart request adding header. |
| // * simple_request_url: one fetch for initially adding header. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 3); |
| // One store for same_origin_iframe_url main frame. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 1); |
| |
| EXPECT_EQ(LastRequestUrl().path(), "/subframe_simple.html"); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SameOriginReduceAcceptLanguageBrowserTest, |
| IframeDifferentContentLanguage) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "zh", |
| .avail_language_in_child = "zh", |
| .vary_in_child = "accept-language"}, |
| {SameOriginIframeUrl(), SimpleRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // Iframe request expect to be the language after language negotiation. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginIframeUrl(), |
| "en-US,en;q=0.9"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure restart happen once. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 1); |
| // Total two different URL requests: |
| // * same_origin_iframe_url: one fetch for initially adding header and another |
| // one for the restart request adding header. |
| // * simple_request_url: one fetch for initially adding header. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 3); |
| // One store for same_origin_iframe_url main frame. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 1); |
| |
| EXPECT_EQ(LastRequestUrl().path(), "/subframe_simple.html"); |
| } |
| |
| class ThirdPartyReduceAcceptLanguageBrowserTest |
| : public ReduceAcceptLanguageBrowserTest, |
| public testing::WithParamInterface<bool> { |
| public: |
| static constexpr char kOtherSiteOriginUrl[] = "https://other-site.com:44445"; |
| static constexpr char kOtherSiteBOriginUrl[] = |
| "https://other-site-b.com:44445"; |
| |
| GURL CrossOriginIframeUrl() const { |
| return GURL( |
| base::StrCat({kFirstPartyOriginUrl, "/cross_origin_iframe.html"})); |
| } |
| |
| GURL TopLevelWithIframeRedirectUrl() const { |
| return GURL(base::StrCat( |
| {kFirstPartyOriginUrl, "/top_level_with_iframe_redirect.html"})); |
| } |
| |
| GURL CrossOriginIframeWithSubresourceUrl() const { |
| return GURL(base::StrCat( |
| {kFirstPartyOriginUrl, "/cross_origin_iframe_with_subrequests.html"})); |
| } |
| |
| GURL SubframeThirdPartyRequestUrl() const { |
| return GURL( |
| base::StrCat({kThirdPartyOriginUrl, "/subframe_redirect_3p.html"})); |
| } |
| |
| GURL SimpleThirdPartyRequestUrl() const { |
| return GURL( |
| base::StrCat({kThirdPartyOriginUrl, "/subframe_simple_3p.html"})); |
| } |
| |
| GURL IframeThirdPartyRequestUrl() const { |
| return GURL( |
| base::StrCat({kThirdPartyOriginUrl, "/subframe_iframe_3p.html"})); |
| } |
| |
| GURL OtherSiteCssRequestUrl() const { |
| return GURL( |
| base::StrCat({kOtherSiteOriginUrl, "/subresource_redirect_style.css"})); |
| } |
| |
| GURL OtherSiteBasicRequestUrl() const { |
| return GURL( |
| base::StrCat({kOtherSiteBOriginUrl, "/subframe_iframe_basic.html"})); |
| } |
| |
| protected: |
| void EnabledFeatures() override { |
| // True: Enable the general feature for Reduce Accept-Language. |
| // False: Only enable reduction for HTTP header. |
| if (GetParam()) { |
| scoped_feature_list_.InitWithFeatures( |
| {network::features::kReduceAcceptLanguage}, {}); |
| } else { |
| scoped_feature_list_.InitWithFeatures( |
| {network::features::kReduceAcceptLanguageHTTP}, |
| {network::features::kReduceAcceptLanguage}); |
| } |
| } |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| ThirdPartyReduceAcceptLanguageBrowserTest, |
| testing::Bool()); |
| |
| IN_PROC_BROWSER_TEST_P(ThirdPartyReduceAcceptLanguageBrowserTest, |
| IframeDifferentContentLanguage) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "zh", |
| .avail_language_in_child = "zh", |
| .vary_in_child = "accept-language"}, |
| {CrossOriginIframeUrl(), SimpleThirdPartyRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // Third party iframe subrequest expect to be the language of the main frame |
| // after language negotiation. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(CrossOriginIframeUrl(), |
| "en-US,en;q=0.9"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure restart happen once. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 1); |
| // Total two different URL requests: |
| // * cross_origin_iframe_url: one fetch for initially adding header and |
| // another one for the restart request adding header. |
| // * simple_3p_request_url: one fetch for initially adding header. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 3); |
| // One store for same_origin_iframe_url main frame. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 1); |
| |
| EXPECT_EQ(LastRequestUrl().path(), "/subframe_simple_3p.html"); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(ThirdPartyReduceAcceptLanguageBrowserTest, |
| ThirdPartyIframeWithSubresourceRequests) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions( |
| {.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "zh", |
| .avail_language_in_child = "zh", |
| .vary_in_child = "accept-language"}, |
| {CrossOriginIframeWithSubresourceUrl(), IframeThirdPartyRequestUrl(), |
| OtherSiteCssRequestUrl(), OtherSiteBasicRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // Third party iframe subrequest expect to be the language of the main frame |
| // after language negotiation. |
| NavigateAndVerifyAcceptLanguageOfLastRequest( |
| CrossOriginIframeWithSubresourceUrl(), "en-US,en;q=0.9"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure restart happen once. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 1); |
| // Fetch reduce accept-language when visiting the following three URLs, for |
| // css request, it won't pass to navigation layer: |
| // * cross_origin_iframe_with_subrequests_url(2):one fetch for initially |
| // adding header and another one for the restart request adding header. |
| // * iframe_3p_request_url(1): one fetch for initially adding header. |
| // * other_site_b_basic_request_url(1): one fetch for initially adding header. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 4); |
| // One store for cross_region_iframe_url main frame. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 1); |
| |
| // All subresources should have been loaded, |
| EXPECT_THAT(intercepted_load_urls_, |
| testing::IsSupersetOf({IframeThirdPartyRequestUrl(), |
| OtherSiteCssRequestUrl(), |
| OtherSiteBasicRequestUrl()})); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(ThirdPartyReduceAcceptLanguageBrowserTest, |
| ThirdPartyIframeWithSubresourceRedirectRequests) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "zh", |
| .avail_language_in_child = "zh", |
| .vary_in_child = "accept-language"}, |
| {TopLevelWithIframeRedirectUrl(), |
| SubframeThirdPartyRequestUrl(), OtherSiteCssRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // It still expected an accept-language header has the reduced value even the |
| // final url is a css style document, |
| NavigateAndVerifyAcceptLanguageOfLastRequest(TopLevelWithIframeRedirectUrl(), |
| "en-US,en;q=0.9"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure restart happen once. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 1); |
| // Fetch reduce accept-language when visiting the following three URLs, for |
| // css request, it won't pass to navigation layer: |
| // * top_level_with_iframe_redirect_url(2):one fetch for initially adding |
| // header and another one for the restart request adding header. |
| // * subframe_3p_request_url(1): one fetch for initially adding header. |
| // * other_site_css_request_url(0): directly read from commit parameter. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 3); |
| // One store for top_level_with_iframe_redirect_url main frame. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 1); |
| |
| // All subresources should have been loaded, |
| EXPECT_THAT(intercepted_load_urls_, |
| testing::IsSupersetOf( |
| {SubframeThirdPartyRequestUrl(), OtherSiteCssRequestUrl()})); |
| } |
| |
| class FencedFrameReduceAcceptLanguageBrowserTest |
| : public ReduceAcceptLanguageBrowserTest { |
| public: |
| static constexpr char kFirstPartyOriginUrl[] = "https://127.0.0.1:44444"; |
| static constexpr char kThirdPartyOriginUrl[] = "https://my-site.com:44444"; |
| |
| GURL SameOriginFencedFrameUrl() const { |
| return GURL( |
| base::StrCat({kFirstPartyOriginUrl, "/same_origin_fenced_frame.html"})); |
| } |
| |
| GURL CrossOriginFencedFrameUrl() const { |
| return GURL(base::StrCat( |
| {kFirstPartyOriginUrl, "/cross_origin_fenced_frame.html"})); |
| } |
| |
| GURL SimpleRequestUrl() const { |
| return GURL(base::StrCat({kFirstPartyOriginUrl, "/subframe_simple.html"})); |
| } |
| |
| GURL SimpleThirdPartyRequestUrl() const { |
| return GURL( |
| base::StrCat({kThirdPartyOriginUrl, "/subframe_simple_3p.html"})); |
| } |
| |
| protected: |
| void EnabledFeatures() override { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| {{blink::features::kFencedFrames, {}}, |
| {blink::features::kFencedFramesAPIChanges, {}}, |
| {blink::features::kFencedFramesDefaultMode, {}}, |
| {features::kPrivacySandboxAdsAPIsOverride, {}}, |
| {network::features::kReduceAcceptLanguage, {}}}, |
| {/* disabled_features */}); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(FencedFrameReduceAcceptLanguageBrowserTest, |
| CrossOriginFencedFrame) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "zh", |
| .avail_language_in_child = "zh", |
| .vary_in_child = "accept-language", |
| .is_fenced_frame = true}, |
| {CrossOriginFencedFrameUrl(), SimpleThirdPartyRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // The result of the main frame's language negotiation should not be shared |
| // with requests made from fenced frames, since fenced frames restrict |
| // communication with their outer page. After language negotiation, the |
| // persisted language is en-US. The third party fenced frame requests should |
| // use the first accept-language zh instead of en-US. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(CrossOriginFencedFrameUrl(), |
| "zh"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure restart happen once. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 1); |
| // Total two different URL requests: |
| // * cross_region_fenced_frame_url(2):one fetch for initially adding |
| // header and another one for the restart request adding header. |
| // * simple_3p_request_url: no fetch for initially adding header since a |
| // fenced frame but not a main frame will result in a nullopt origin value |
| // when getting top-level main frame origin. In this case, we set the |
| // Accept-Language header with the first user’s accept-language. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 2); |
| // One store for cross_region_fenced_frame_url main frame. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 1); |
| |
| EXPECT_EQ(LastRequestUrl().path(), "/subframe_simple_3p.html"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(FencedFrameReduceAcceptLanguageBrowserTest, |
| SameOriginFencedFrame) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "zh", |
| .avail_language_in_child = "zh", |
| .vary_in_child = "accept-language", |
| .is_fenced_frame = true}, |
| {SameOriginFencedFrameUrl(), SimpleRequestUrl()}); |
| |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // Main frame after language negotiation should not shared to fenced frame |
| // subrequest since restricts communication. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginFencedFrameUrl(), |
| "zh"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure restart happen once. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 1); |
| // Total two different URL requests: |
| // * same_origin_fenced_frame_url(2):one fetch for initially adding |
| // header and another one for the restart request adding header. |
| // * simple_request_url: no fetch for initially adding header since a fenced |
| // frame but not a main frame will result in a nullopt origin value when |
| // getting top-level main frame origin. In this case, we set the |
| // Accept-Language header with the first user’s accept-language. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 2); |
| // One store for cross_region_fenced_frame_url main frame. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 1); |
| |
| EXPECT_EQ("/subframe_simple.html", LastRequestUrl().path()); |
| } |
| |
| // Browser tests verify redirect same origin with different cases. |
| class SameOriginRedirectReduceAcceptLanguageBrowserTest |
| : public ReduceAcceptLanguageBrowserTest { |
| public: |
| explicit SameOriginRedirectReduceAcceptLanguageBrowserTest( |
| const std::vector<ServerPortAndValidOriginToken>& port_tokens = {}) |
| : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) { |
| https_server_.ServeFilesFromSourceDirectory( |
| "chrome/test/data/reduce_accept_language"); |
| |
| https_server_.RegisterRequestMonitor( |
| base::BindRepeating(&SameOriginRedirectReduceAcceptLanguageBrowserTest:: |
| MonitorResourceRequest, |
| base::Unretained(this))); |
| |
| https_server_.RegisterRequestHandler( |
| base::BindRepeating(&SameOriginRedirectReduceAcceptLanguageBrowserTest:: |
| RequestHandlerRedirect, |
| base::Unretained(this))); |
| |
| // Using a specified port for origin trial to generate token instead of |
| // always using an auto selected one. |
| StartTestServerAndSetToken(&https_server_, port_tokens); |
| |
| same_origin_redirect_ = https_server_.GetURL("/same_origin_redirect.html"); |
| same_origin_redirect_a_ = |
| https_server_.GetURL("/same_origin_redirect_a.html"); |
| same_origin_redirect_b_ = |
| https_server_.GetURL("/same_origin_redirect_b.html"); |
| } |
| |
| static constexpr const char kAcceptLanguage[] = "accept-language"; |
| static constexpr auto kValidPaths = base::MakeFixedFlatSet<std::string_view>({ |
| "/same_origin_redirect.html", |
| "/same_origin_redirect_a.html", |
| "/same_origin_redirect_b.html", |
| }); |
| |
| GURL same_origin_redirect() const { return same_origin_redirect_; } |
| |
| GURL same_origin_redirect_a() const { return same_origin_redirect_a_; } |
| |
| GURL same_origin_redirect_b() const { return same_origin_redirect_b_; } |
| |
| void SetOptions(const std::string& content_language_a, |
| const std::string& content_language_b) { |
| content_language_a_ = content_language_a; |
| content_language_b_ = content_language_b; |
| } |
| |
| protected: |
| void EnabledFeatures() override { |
| // Explicit enable feature ReduceAcceptLanguage. |
| scoped_feature_list_.InitAndEnableFeature( |
| {network::features::kReduceAcceptLanguage}); |
| } |
| |
| private: |
| // Intercepts only the requests that for same origin redirect tests. |
| std::unique_ptr<net::test_server::HttpResponse> RequestHandlerRedirect( |
| const net::test_server::HttpRequest& request) { |
| if (!base::Contains(kValidPaths, request.relative_url)) |
| return nullptr; |
| |
| std::string accept_language; |
| if (request.headers.find(kAcceptLanguage) != request.headers.end()) { |
| accept_language = |
| GetFirstLanguage(request.headers.find(kAcceptLanguage)->second); |
| } |
| |
| auto response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| if (request.relative_url == "/same_origin_redirect.html") { |
| response->set_code(net::HTTP_FOUND); |
| // Assume site supports content_language_a_ and content_language_b_. If |
| // accept-language matches content_language_b_ then returns |
| // content_language_b_, otherwise returns content_language_a_. |
| if (accept_language == content_language_b_) { |
| response->AddCustomHeader("Content-Language", content_language_b_); |
| response->AddCustomHeader("Location", same_origin_redirect_b().spec()); |
| } else { |
| response->AddCustomHeader("Content-Language", content_language_a_); |
| response->AddCustomHeader("Location", same_origin_redirect_a().spec()); |
| } |
| } else if (request.relative_url == "/same_origin_redirect_a.html") { |
| response->set_code(net::HTTP_OK); |
| response->AddCustomHeader("Content-Language", content_language_a_); |
| } else if (request.relative_url == "/same_origin_redirect_b.html") { |
| response->set_code(net::HTTP_OK); |
| response->AddCustomHeader("Content-Language", content_language_b_); |
| } |
| |
| response->AddCustomHeader( |
| "Avail-Language", |
| base::StrCat({content_language_a_, ", ", content_language_b_})); |
| |
| if (!origin_trial_first_party_token_.empty()) { |
| response->AddCustomHeader("Origin-Trial", |
| origin_trial_first_party_token_); |
| } |
| |
| return std::move(response); |
| } |
| |
| // Called by `https_server_`. |
| void MonitorResourceRequest(const net::test_server::HttpRequest& request) { |
| if (!base::Contains(kValidPaths, request.relative_url)) |
| return; |
| |
| if (request.headers.find(kAcceptLanguage) != request.headers.end()) { |
| actual_url_accept_language_.push_back( |
| {request.GetURL().spec(), |
| request.headers.find(kAcceptLanguage)->second}); |
| } |
| } |
| |
| GURL same_origin_redirect_; |
| GURL same_origin_redirect_a_; |
| GURL same_origin_redirect_b_; |
| net::EmbeddedTestServer https_server_; |
| std::string content_language_a_; |
| std::string content_language_b_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(SameOriginRedirectReduceAcceptLanguageBrowserTest, |
| MatchFirstLanguage) { |
| SetPrefsAcceptLanguage({"en", "ja"}); |
| SetOptions(/*content_language_a=*/"en", /*content_language_b=*/"ja"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), same_origin_redirect())); |
| |
| // 1. initial request to main request(/) with first user accept-language en. |
| // 2. initial request to A(/en) with the language matches the expected |
| // accept-language. |
| VerifyURLAndAcceptLanguageSequence({{same_origin_redirect().spec(), "en"}, |
| {same_origin_redirect_a().spec(), "en"}}); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SameOriginRedirectReduceAcceptLanguageBrowserTest, |
| MatchSecondaryLanguage) { |
| SetPrefsAcceptLanguage({"zh-CN", "ja"}); |
| SetOptions(/*content_language_a=*/"en", /*content_language_b=*/"ja"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), same_origin_redirect())); |
| |
| // 1. initial request to main request(/) with first user accept-language |
| // zh-CN. |
| // 2. restart request to main request(/) with the persisted language ja after |
| // language negotiation. |
| // 3. initial request to B(/ja) with the language matches the expected |
| // accept-language. |
| VerifyURLAndAcceptLanguageSequence( |
| {{same_origin_redirect().spec(), "zh-CN,zh;q=0.9"}, |
| {same_origin_redirect().spec(), "ja"}, |
| {same_origin_redirect_b().spec(), "ja"}}); |
| } |
| |
| // Browser tests verify redirect cross origin A to B with different cases. |
| class CrossOriginRedirectReduceAcceptLanguageBrowserTest |
| : public ReduceAcceptLanguageBrowserTest { |
| public: |
| explicit CrossOriginRedirectReduceAcceptLanguageBrowserTest( |
| const std::vector<ServerPortAndValidOriginToken>& port_tokens_a = {}, |
| const std::vector<ServerPortAndValidOriginToken>& port_tokens_b = {}) |
| : https_server_a_(net::EmbeddedTestServer::TYPE_HTTPS), |
| https_server_b_(net::EmbeddedTestServer::TYPE_HTTPS) { |
| https_server_a_.ServeFilesFromSourceDirectory( |
| "chrome/test/data/reduce_accept_language"); |
| https_server_b_.ServeFilesFromSourceDirectory( |
| "chrome/test/data/reduce_accept_language"); |
| |
| https_server_a_.RegisterRequestMonitor(base::BindRepeating( |
| &CrossOriginRedirectReduceAcceptLanguageBrowserTest:: |
| MonitorResourceRequest, |
| base::Unretained(this))); |
| |
| https_server_a_.RegisterRequestHandler(base::BindRepeating( |
| &CrossOriginRedirectReduceAcceptLanguageBrowserTest:: |
| RequestHandlerRedirect, |
| base::Unretained(this))); |
| |
| https_server_b_.RegisterRequestMonitor(base::BindRepeating( |
| &CrossOriginRedirectReduceAcceptLanguageBrowserTest:: |
| MonitorResourceRequest, |
| base::Unretained(this))); |
| https_server_b_.RegisterRequestHandler(base::BindRepeating( |
| &CrossOriginRedirectReduceAcceptLanguageBrowserTest:: |
| RequestHandlerRedirect, |
| base::Unretained(this))); |
| |
| // Using a specified port for origin trial to generate token instead of |
| // always using an auto selected one. |
| StartTestServerAndSetToken(&https_server_a_, port_tokens_a); |
| StartTestServerAndSetToken(&https_server_b_, port_tokens_b, true); |
| |
| // Make sure two origins are different. |
| EXPECT_NE(https_server_a_.base_url(), https_server_b_.base_url()); |
| cross_origin_redirect_a_ = |
| https_server_a_.GetURL("/cross_origin_redirect_a.html"); |
| cross_origin_redirect_b_ = |
| https_server_b_.GetURL("/cross_origin_redirect_b.html"); |
| } |
| |
| static constexpr const char kAcceptLanguage[] = "accept-language"; |
| static constexpr auto kValidPaths = base::MakeFixedFlatSet<std::string_view>({ |
| "/cross_origin_redirect_a.html", |
| "/cross_origin_redirect_b.html", |
| }); |
| |
| GURL cross_origin_redirect_a() const { return cross_origin_redirect_a_; } |
| |
| GURL cross_origin_redirect_b() const { return cross_origin_redirect_b_; } |
| |
| void SetOptions(const std::vector<std::string> avail_language_a, |
| const std::vector<std::string> avail_language_b) { |
| avail_language_a_ = avail_language_a; |
| avail_language_b_ = avail_language_b; |
| } |
| |
| void SetOriginTrialFirstPartyToken(const std::string& origin_trial_token_a, |
| const std::string& origin_trial_token_b) { |
| origin_trial_token_a_ = origin_trial_token_a; |
| origin_trial_token_b_ = origin_trial_token_b; |
| } |
| |
| protected: |
| void EnabledFeatures() override { |
| std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); |
| feature_list->InitFromCommandLine("ReduceAcceptLanguage", ""); |
| scoped_feature_list_.InitWithFeatureList(std::move(feature_list)); |
| } |
| |
| private: |
| // Intercepts only the requests that for cross origin redirect tests. |
| std::unique_ptr<net::test_server::HttpResponse> RequestHandlerRedirect( |
| const net::test_server::HttpRequest& request) { |
| if (!base::Contains(kValidPaths, request.relative_url)) |
| return nullptr; |
| |
| std::string accept_language; |
| if (request.headers.find(kAcceptLanguage) != request.headers.end()) |
| accept_language = request.headers.find(kAcceptLanguage)->second; |
| |
| auto response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| if (request.relative_url == "/cross_origin_redirect_a.html") { |
| response->set_code(net::HTTP_FOUND); |
| response->AddCustomHeader( |
| "Content-Language", |
| GetResponseContentLanguage(accept_language, avail_language_a_)); |
| |
| // Stop sending Avail-Language header as well if tests set an invalid |
| // origin token. |
| response->AddCustomHeader("Avail-Language", |
| base::JoinString(avail_language_a_, ", ")); |
| |
| response->AddCustomHeader("Location", cross_origin_redirect_b().spec()); |
| if (!origin_trial_token_a_.empty()) { |
| response->AddCustomHeader("Origin-Trial", origin_trial_token_a_); |
| } |
| } else if (request.relative_url == "/cross_origin_redirect_b.html") { |
| response->set_code(net::HTTP_OK); |
| response->AddCustomHeader( |
| "Content-Language", |
| GetResponseContentLanguage(accept_language, avail_language_b_)); |
| response->AddCustomHeader("Avail-Language", |
| base::JoinString(avail_language_b_, ", ")); |
| if (!origin_trial_token_b_.empty()) { |
| response->AddCustomHeader("Origin-Trial", origin_trial_token_b_); |
| } |
| } |
| return std::move(response); |
| } |
| |
| // Called by `https_server_`. |
| void MonitorResourceRequest(const net::test_server::HttpRequest& request) { |
| if (!base::Contains(kValidPaths, request.relative_url)) |
| return; |
| |
| if (request.headers.find(kAcceptLanguage) != request.headers.end()) { |
| actual_url_accept_language_.push_back( |
| {request.GetURL().spec(), |
| request.headers.find(kAcceptLanguage)->second}); |
| } |
| } |
| |
| GURL cross_origin_redirect_a_; |
| GURL cross_origin_redirect_b_; |
| net::EmbeddedTestServer https_server_a_; |
| net::EmbeddedTestServer https_server_b_; |
| std::vector<std::string> avail_language_a_; |
| std::vector<std::string> avail_language_b_; |
| std::string origin_trial_token_a_; |
| std::string origin_trial_token_b_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(CrossOriginRedirectReduceAcceptLanguageBrowserTest, |
| RestartOnA) { |
| SetPrefsAcceptLanguage({"en-US", "zh"}); |
| SetOptions(/*avail_language_a=*/{"ja", "zh"}, |
| /*avail_language_b=*/{"en-US"}); |
| |
| // initial redirect request. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| |
| // 1. initial request to A with first user accept-language en-US. |
| // 2. restart request to A with the persisted language zh. |
| // 3. initial request to B with the first user accept-language en-US. |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_a().spec(), "zh"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9"}}); |
| |
| ResetURLAndAcceptLanguageSequence(); |
| |
| // Secondary redirect request expects no restarts. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "zh"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9"}}); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(CrossOriginRedirectReduceAcceptLanguageBrowserTest, |
| RestartOnB) { |
| SetPrefsAcceptLanguage({"en-US", "zh"}); |
| SetOptions(/*avail_language_a=*/{"en-US", "zh"}, |
| /*avail_language_b=*/{"de", "zh"}); |
| |
| // initial redirect request. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| |
| // 1. initial request to A with first user accept-language en-US. |
| // 2. initial request to B with the first user accept-language en-US. |
| // 3. restart request to A with first user accept-language en-US. |
| // 4. restart request to B with the persisted language zh. |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_a().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_b().spec(), "zh"}}); |
| |
| ResetURLAndAcceptLanguageSequence(); |
| |
| // Secondary redirect request expects no restarts. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_b().spec(), "zh"}}); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(CrossOriginRedirectReduceAcceptLanguageBrowserTest, |
| RestartBothAB) { |
| SetPrefsAcceptLanguage({"en-US", "zh"}); |
| SetOptions(/*avail_language_a=*/{"ja", "zh"}, |
| /*avail_language_b=*/{"de", "zh"}); |
| |
| // initial redirect request. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| |
| // 1. initial request to A with first user accept-language en-US. |
| // 2. restart request to A with the persisted language zh. |
| // 3. initial request to B with the first user accept-language en-US. |
| // 4. restart request to A since redirect the original URL with persisted |
| // language zh. |
| // 5. restart request to B with the persisted language zh. |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_a().spec(), "zh"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_a().spec(), "zh"}, |
| {cross_origin_redirect_b().spec(), "zh"}}); |
| |
| ResetURLAndAcceptLanguageSequence(); |
| |
| // Secondary redirect request expects no restarts. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "zh"}, |
| {cross_origin_redirect_b().spec(), "zh"}}); |
| } |
| |
| // Browser tests verify same origin redirect when DisableReduceAcceptLanguage |
| // deprecation origin trial enable. |
| // NOTES: As URLLoaderInterceptor doesn't support redirect, testing redirects |
| // with origin trial requires EmbeddedTestServer to start on specific ports, we |
| // can only add a single test in this test class in case the different tests run |
| // parallel to cause server can't starts on specific ports. It will cause tests |
| // flakiness. Also, we need to make sure it doesn't share port with any other |
| // browser_tests. |
| class SameOriginRedirectReduceAcceptLanguageOTBrowserTest |
| : public SameOriginRedirectReduceAcceptLanguageBrowserTest { |
| public: |
| SameOriginRedirectReduceAcceptLanguageOTBrowserTest() |
| : SameOriginRedirectReduceAcceptLanguageBrowserTest( |
| GetValidPortsAndTokens()) { |
| // Initialize with valid origin trial token. |
| SetOriginTrialFirstPartyToken(GetValidFirstPartyToken()); |
| } |
| |
| // Work around solution to test redirect using EmbeddedTestServer. Make a list |
| // port and corresponding OT token for test server to retry if port in use. |
| // generate_token.py https://127.0.0.1:44455 DisableReduceAcceptLanguage |
| // --expire-timestamp=2000000000 |
| const std::vector<ServerPortAndValidOriginToken>& GetValidPortsAndTokens() { |
| static const base::NoDestructor<std::vector<ServerPortAndValidOriginToken>> |
| vec({ |
| {44455, |
| "A1IOD8fXBTZ01WHMaBk9MqqvmiLPtIioHYEpcPn7kLtRHqJNL4pwZguJErdl+" |
| "hIXpDYbR+" |
| "7VnhXtv7YtEyaJzgoAAABleyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE6NDQ" |
| "0NTUiLCAiZmVhdHVyZSI6ICJEaXNhYmxlUmVkdWNlQWNjZXB0TGFuZ3VhZ2UiLCAi" |
| "ZXhwaXJ5IjogMjAwMDAwMDAwMH0="}, |
| {44456, |
| "A+QbN+WXtJCFPhyFkS2uW0VU3DdceOtQvO/" |
| "8ZYL9CgicyLVyZQngYWZeahtT2Hy3978TOwrCD7D+" |
| "AJGo1eseqAcAAABleyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE6NDQ0NTYiL" |
| "CAiZmVhdHVyZSI6ICJEaXNhYmxlUmVkdWNlQWNjZXB0TGFuZ3VhZ2UiLCAiZXhwaX" |
| "J5IjogMjAwMDAwMDAwMH0="}, |
| {44457, |
| "A75oT3Ki0N9WCQNOlzmB8+1s3pJMdNIT1DqeXkjF1LF8Xg6rfK65Z/" |
| "bDKuBzDNMhTgoD5fN+" |
| "RBRcVG8jLLWhSQ0AAABleyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE6NDQ0N" |
| "TciLCAiZmVhdHVyZSI6ICJEaXNhYmxlUmVkdWNlQWNjZXB0TGFuZ3VhZ2UiLCAiZX" |
| "hwaXJ5IjogMjAwMDAwMDAwMH0="}, |
| {44458, |
| "A3tN+D5Qyma6ozNdZPQIyu32bgG1Nwb3rQzwP8Su+" |
| "57FbFQTGSXu3Wpr0HHOhkdKk50FVs849XJEv1pMjwDb7QQAAABleyJvcmlnaW4iOi" |
| "AiaHR0cHM6Ly8xMjcuMC4wLjE6NDQ0NTgiLCAiZmVhdHVyZSI6ICJEaXNhYmxlUmV" |
| "kdWNlQWNjZXB0TGFuZ3VhZ2UiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0="}, |
| {44459, |
| "A2bAXhaCHO5WNgU4xkHLG7UeYzD4lVlXBJxAO0/7U/" |
| "Rie3s82v4AfWaOCZOd0YvOgxoTw8WVQZObpgIGkyQrQgYAAABleyJvcmlnaW4iOiA" |
| "iaHR0cHM6Ly8xMjcuMC4wLjE6NDQ0NTkiLCAiZmVhdHVyZSI6ICJEaXNhYmxlUmVk" |
| "dWNlQWNjZXB0TGFuZ3VhZ2UiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0="}, |
| }); |
| return *vec; |
| } |
| |
| std::string GetValidFirstPartyToken() { return valid_first_party_token_; } |
| |
| protected: |
| void EnabledFeatures() override { |
| // Explicit enable feature ReduceAcceptLanguage. |
| scoped_feature_list_.InitAndEnableFeature( |
| {network::features::kReduceAcceptLanguage}); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(SameOriginRedirectReduceAcceptLanguageOTBrowserTest, |
| MatchFirstLanguage) { |
| // Match the first language |
| SetPrefsAcceptLanguage({"en", "ja"}); |
| SetOptions(/*content_language_a=*/"en", /*content_language_b=*/"ja"); |
| SetOriginTrialFirstPartyToken(GetValidFirstPartyToken()); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), same_origin_redirect())); |
| |
| // First Request, opt-in the deprecation origin trial. |
| // 1. initial request to main request(/) with reduced accept-language since |
| // we can't validate deprecation origin trial token before sending requests. |
| // 2. initial request to A(/en) with the reduced language en which persisted |
| // when process request to main request(/). |
| VerifyURLAndAcceptLanguageSequence( |
| {{same_origin_redirect().spec(), "en"}, |
| {same_origin_redirect_a().spec(), "en"}}, |
| "Verifying the first request sequence failed in matching first " |
| "language."); |
| |
| // Second request. |
| ResetURLAndAcceptLanguageSequence(); |
| // 1. Second request to main request(/) with the unreduced accept-language. |
| // 2. Second request to A(/en) with the unreduced accept-language en. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), same_origin_redirect())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{same_origin_redirect().spec(), "en,ja;q=0.9"}, |
| {same_origin_redirect_a().spec(), "en,ja;q=0.9"}}, |
| "Verifying the second request sequence failed in matching first " |
| "language."); |
| |
| // Third Request: reset deprecation origin trial token to be invalid, opt-out |
| // deprecation trial. |
| SetOriginTrialFirstPartyToken(kInvalidOriginToken); |
| ResetURLAndAcceptLanguageSequence(); |
| // 1. Third request to main request(/) with the unreduced accept-language. |
| // 2. Third request to A(/en) with the unreduced accept-language. |
| // All persisted languages for the givin origin should be cleaned in this |
| // request, all subsequent requests should start sending unreduced |
| // accept-language. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), same_origin_redirect())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{same_origin_redirect().spec(), "en,ja;q=0.9"}, |
| {same_origin_redirect_a().spec(), "en,ja;q=0.9"}}, |
| "Verifying the third request sequence failed in matching first " |
| "language."); |
| |
| // Fourth request. |
| ResetURLAndAcceptLanguageSequence(); |
| // 1. Fourth request to main request(/) with the reduced accept-language en. |
| // 2. Fourth request to A(/en) with the reduced accept-language en. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), same_origin_redirect())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{same_origin_redirect().spec(), "en"}, |
| {same_origin_redirect_a().spec(), "en"}}, |
| "Verifying the fourth request sequence failed in matching first " |
| "language."); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SameOriginRedirectReduceAcceptLanguageOTBrowserTest, |
| MatchNonPrimaryLanguage) { |
| // Match non primary language |
| SetPrefsAcceptLanguage({"zh-CN", "ja"}); |
| SetOptions(/*content_language_a=*/"en", /*content_language_b=*/"ja"); |
| SetOriginTrialFirstPartyToken(GetValidFirstPartyToken()); |
| |
| ResetURLAndAcceptLanguageSequence(); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), same_origin_redirect())); |
| |
| // First Request, opt-in the deprecation origin trial. |
| // 1. initial request to main request(/) with reduced accept-language zh-CN |
| // since we can't validate deprecation origin trial token before sending |
| // requests. |
| // 2. restart request to main request(/) with the persisted language ja after |
| // language negotiation. |
| // 3. initial request to B(/ja) with the language matches the expected |
| // accept-language. |
| VerifyURLAndAcceptLanguageSequence( |
| {{same_origin_redirect().spec(), "zh-CN,zh;q=0.9"}, |
| {same_origin_redirect().spec(), "ja"}, |
| {same_origin_redirect_b().spec(), "ja"}}, |
| "Verifying the first request sequence failed in matching non-primary " |
| "language."); |
| |
| // Second request. |
| ResetURLAndAcceptLanguageSequence(); |
| // 1. Second request to main request(/) with unreduced accept-language. |
| // 2. Second request to B(/ja) with unreduced accept-language. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), same_origin_redirect())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{same_origin_redirect().spec(), "zh-CN,zh;q=0.9,ja;q=0.8"}, |
| {same_origin_redirect_a().spec(), "zh-CN,zh;q=0.9,ja;q=0.8"}}, |
| "Verifying the second request sequence failed in matching non-primary " |
| "language."); |
| |
| // Third Request: reset deprecation origin trial token to be invalid, this is |
| // also the first request opt-out deprecation trial. |
| SetOriginTrialFirstPartyToken(kInvalidOriginToken); |
| ResetURLAndAcceptLanguageSequence(); |
| // 1. Third request to main request(/) with unreduced accept-language. |
| // 2. Third request to A(/en) with unreduced accept-language. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), same_origin_redirect())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{same_origin_redirect().spec(), "zh-CN,zh;q=0.9,ja;q=0.8"}, |
| {same_origin_redirect_a().spec(), "zh-CN,zh;q=0.9,ja;q=0.8"}}, |
| "Verifying the third request sequence failed in matching non-primary " |
| "language."); |
| |
| // Fourth request. |
| ResetURLAndAcceptLanguageSequence(); |
| // 1. Fourth request to main request(/) with reduced accept-language zh-CN |
| // since site opt-out the deprecation origin trial. |
| // 2. restart request to main request(/) with the persisted language ja after |
| // language negotiation. |
| // 3. Fourth request to B(/ja) with the language matches the expected |
| // accept-language. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), same_origin_redirect())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{same_origin_redirect().spec(), "zh-CN,zh;q=0.9"}, |
| {same_origin_redirect().spec(), "ja"}, |
| {same_origin_redirect_b().spec(), "ja"}}, |
| "Verifying the fourth request sequence failed in matching non-primary " |
| "language."); |
| } |
| |
| // Browser tests verify cross origin redirect when DisableReduceAcceptLanguage |
| // origin trial enable. |
| // NOTES: As URLLoaderInterceptor doesn't support redirect, testing redirects |
| // with origin trial requires EmbeddedTestServer to start on specific ports, we |
| // can only add a single test in this test class in case the different tests run |
| // parallel to cause server can't starts on specific ports. It will cause tests |
| // flakiness. Also, we need to make sure it doesn't share port with any other |
| // browser_tests. |
| class CrossOriginRedirectReduceAcceptLanguageOTBrowserTest |
| : public CrossOriginRedirectReduceAcceptLanguageBrowserTest { |
| public: |
| CrossOriginRedirectReduceAcceptLanguageOTBrowserTest() |
| : CrossOriginRedirectReduceAcceptLanguageBrowserTest( |
| GetValidPortsAndTokensA(), |
| GetValidPortsAndTokensB()) {} |
| |
| // generate_token.py https://127.0.0.1:44466 DisableReduceAcceptLanguage |
| // --expire-timestamp=2000000000 |
| const std::vector<ServerPortAndValidOriginToken>& GetValidPortsAndTokensA() { |
| static const base::NoDestructor<std::vector<ServerPortAndValidOriginToken>> |
| vec({ |
| {44466, |
| "A3yx7PpTSg5saq9Ni4by9/" |
| "8lCmxy9kTkz9l2qcVCWTxE7m1hF5JNBJQnrnva5dEI0iozq08H+TsS3bFRptQ+" |
| "vg0AAABleyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE6NDQ0NjYiLCAiZmVhd" |
| "HVyZSI6ICJEaXNhYmxlUmVkdWNlQWNjZXB0TGFuZ3VhZ2UiLCAiZXhwaXJ5IjogMj" |
| "AwMDAwMDAwMH0="}, |
| {44467, |
| "A8uLuFYjCwhU1tNYQBC6JsK7QtZTrYe1QOeSU/irQMdmOaU/" |
| "dXv6n7JWxS7vsQgEApOWyc58HVlIxr3TT8rT3A0AAABleyJvcmlnaW4iOiAiaHR0c" |
| "HM6Ly8xMjcuMC4wLjE6NDQ0NjciLCAiZmVhdHVyZSI6ICJEaXNhYmxlUmVkdWNlQW" |
| "NjZXB0TGFuZ3VhZ2UiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0="}, |
| {44468, |
| "AyRZGI32JZcK6DYat+TahnkxJ+nrT/" |
| "G9vw9DkmVMmMF7IZLFQ9PC3NKZ9Votiik9sGY+" |
| "PSV8odfsLrqZtpHQzAwAAABleyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE6N" |
| "DQ0NjgiLCAiZmVhdHVyZSI6ICJEaXNhYmxlUmVkdWNlQWNjZXB0TGFuZ3VhZ2UiLC" |
| "AiZXhwaXJ5IjogMjAwMDAwMDAwMH0="}, |
| {44469, |
| "AxViEPVpoX+1KbTBnBv0HZoI5dukMCi0Ib4FQYPzadSdg3XWaFv+" |
| "CnvdtWl7IyjQjLuO0v3cnFs797PjUrcDoQwAAABleyJvcmlnaW4iOiAiaHR0cHM6L" |
| "y8xMjcuMC4wLjE6NDQ0NjkiLCAiZmVhdHVyZSI6ICJEaXNhYmxlUmVkdWNlQWNjZX" |
| "B0TGFuZ3VhZ2UiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0="}, |
| {44470, |
| "A657dEV4mXzjMLET+T6Z/" |
| "iHO9WbXi+i7aC7g42WzY8I96tDKGGM6On4vKhqB6VlntM/Ec0iIw2DJ3/" |
| "VrNMOpKg4AAABleyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE6NDQ0NzAiLCA" |
| "iZmVhdHVyZSI6ICJEaXNhYmxlUmVkdWNlQWNjZXB0TGFuZ3VhZ2UiLCAiZXhwaXJ5" |
| "IjogMjAwMDAwMDAwMH0="}, |
| }); |
| return *vec; |
| } |
| |
| // generate_token.py https://127.0.0.1:44477 DisableReduceAcceptLanguage |
| // --expire-timestamp=2000000000 |
| const std::vector<ServerPortAndValidOriginToken>& GetValidPortsAndTokensB() { |
| static const base::NoDestructor<std::vector<ServerPortAndValidOriginToken>> |
| vec({ |
| {44477, |
| "A6P0N8LB9c/" |
| "ZmFPpIyoPk7cSL3wv5adWfrmFI0FNfvY672xO96N9e5tLK2MtHm89YH2QIp4ROfow" |
| "krIlh2OzxgIAAABleyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE6NDQ0NzciL" |
| "CAiZmVhdHVyZSI6ICJEaXNhYmxlUmVkdWNlQWNjZXB0TGFuZ3VhZ2UiLCAiZXhwaX" |
| "J5IjogMjAwMDAwMDAwMH0="}, |
| {44478, |
| "A1hljUHw96l+l4zSiLZjGwCdEJ8jMdZp0GJnBJIHO9nCvP6KiXJi/" |
| "Ow5yRLy+" |
| "8RtrWMyyTJVDaX3fbJCFpcavg0AAABleyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC" |
| "4wLjE6NDQ0NzgiLCAiZmVhdHVyZSI6ICJEaXNhYmxlUmVkdWNlQWNjZXB0TGFuZ3V" |
| "hZ2UiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0="}, |
| {44479, |
| "A2mynuvz4OL0gAkgDO1SNfLrAL7Mpb1aKJBzZ7TMby/" |
| "nZQNEXed1Cr9mDAWoG1Kj6sD3ygKPgm68dnTo+" |
| "7ujDQUAAABleyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE6NDQ0NzkiLCAiZm" |
| "VhdHVyZSI6ICJEaXNhYmxlUmVkdWNlQWNjZXB0TGFuZ3VhZ2UiLCAiZXhwaXJ5Ijo" |
| "gMjAwMDAwMDAwMH0="}, |
| {44480, |
| "A0qwpXBuq0NOJjSypFvI2O59dKzJSGZKLXSgPYDS2N7IjX7nBHBtISWbGRXxm24QL" |
| "puxWAUxyQ/" |
| "RTdB4uGdy3AMAAABleyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE6NDQ0ODAi" |
| "LCAiZmVhdHVyZSI6ICJEaXNhYmxlUmVkdWNlQWNjZXB0TGFuZ3VhZ2UiLCAiZXhwa" |
| "XJ5IjogMjAwMDAwMDAwMH0="}, |
| {44481, |
| "A/aKNOr1iw10YYvsrJPF4TMpMFiyGUk/qGw3uUk4ZD/" |
| "t0TFjxVZa7NsdLr5jFAVDT+" |
| "aWyWTDu41hyBqkjWIrlQgAAABleyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE" |
| "6NDQ0ODEiLCAiZmVhdHVyZSI6ICJEaXNhYmxlUmVkdWNlQWNjZXB0TGFuZ3VhZ2Ui" |
| "LCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0="}, |
| }); |
| return *vec; |
| } |
| |
| std::string GetValidTokenA() { return valid_first_party_token_; } |
| |
| std::string GetValidTokenB() { return valid_third_party_token_; } |
| |
| protected: |
| void EnabledFeatures() override { |
| // Explicit enable feature ReduceAcceptLanguage. |
| scoped_feature_list_.InitAndEnableFeature( |
| {network::features::kReduceAcceptLanguage}); |
| } |
| }; |
| |
| // Persistent origin trial doesn't works when a.com redirects to b.com and only |
| // a.com opt-in the deprecation origin trial, because persistent origin trial |
| // only parse and persist token for commit origin, in this redirect case b.com |
| // is always the commit origin. |
| IN_PROC_BROWSER_TEST_F(CrossOriginRedirectReduceAcceptLanguageOTBrowserTest, |
| RestartOnA) { |
| // Restart only happens on A, and only A opt-in the deprecation origin trial, |
| // then invalidate only A's token. |
| SetPrefsAcceptLanguage({"en-US", "zh"}); |
| SetOptions(/*avail_language_a=*/{"ja", "zh"}, |
| /*avail_language_b=*/{"en-US"}); |
| |
| // Set A opt-in and B opt-out the origin trial. |
| SetOriginTrialFirstPartyToken( |
| /*origin_trial_token_a=*/GetValidTokenA(), |
| /*origin_trial_token_b=*/kInvalidOriginToken); |
| |
| ResetURLAndAcceptLanguageSequence(); |
| // initial redirect request. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| // 1. initial request to A with with reduced accept-language en-US since |
| // we can't validate deprecation origin trial token before sending requests. |
| // 2. restart request to A with the persisted language zh. |
| // 3. initial request to B with reduced user accept-language en-US. |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_a().spec(), "zh"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9"}}, |
| "Verifying RestartOnA the first request sequence failed."); |
| |
| ResetURLAndAcceptLanguageSequence(); |
| // Secondary redirect request expects no restarts, but it won't send unreduced |
| // accept-language. Persistent origin trial won't persist origin A's token in |
| // case a.com redirects to b.com since it only persist the token for the |
| // commit origin, in the redirect case, b.com is the commit origin. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "zh"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9"}}, |
| "Verifying RestartOnA the second request sequence failed."); |
| |
| // Set A opt-out the deprecation origin trial. |
| SetOriginTrialFirstPartyToken(/*origin_trial_token_a=*/kInvalidOriginToken, |
| /*origin_trial_token_b=*/kInvalidOriginToken); |
| |
| base::HistogramTester histograms; |
| ResetURLAndAcceptLanguageSequence(); |
| // Accept-Language in the third request header is the same as the second one, |
| // it won't clear the persisted language since we can't validate a.com |
| // deprecation trial token. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "zh"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9"}}, |
| "Verifying RestartOnA the third request sequence failed."); |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.ClearLatency", 0); |
| |
| ResetURLAndAcceptLanguageSequence(); |
| // Fourth request will be the same as the third one. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "zh"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9"}}, |
| "Verifying RestartOnA the fourth request sequence failed."); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(CrossOriginRedirectReduceAcceptLanguageOTBrowserTest, |
| RestartOnB) { |
| // Restart only happens on B, and only B opt-in the deprecation origin trial, |
| // then invalidate only B's token. |
| SetPrefsAcceptLanguage({"en-US", "zh"}); |
| SetOptions(/*avail_language_a=*/{"en-US", "zh"}, |
| /*avail_language_b=*/{"de", "zh"}); |
| |
| // Set B opt-in and A opt-out the origin trial. |
| SetOriginTrialFirstPartyToken(/*origin_trial_token_a=*/kInvalidOriginToken, |
| /*origin_trial_token_b=*/GetValidTokenB()); |
| |
| ResetURLAndAcceptLanguageSequence(); |
| // Initial request. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| // 1. initial request to A with the reduced accept-language en-US since A |
| // hasn't participated in the deprecation origin trial. |
| // 2. initial request to B with reduced accept-language en-US since we |
| // can't validate B's deprecation origin trial token before sending requests. |
| // 3. restart request to A still sends the reduced accept-language. |
| // 4. restart request to B with the persisted language zh. |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_a().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_b().spec(), "zh"}}, |
| "Verifying RestartOnB the first request sequence failed."); |
| |
| ResetURLAndAcceptLanguageSequence(); |
| // Secondary redirect request expects no restarts, A sends reduced |
| // Accept-Language and B sends unreduced Accept-Language header. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9,zh;q=0.8"}}, |
| "Verifying RestartOnB the second request sequence failed."); |
| |
| // Set B opt-out the origin trial. |
| SetOriginTrialFirstPartyToken(/*origin_trial_token_a=*/kInvalidOriginToken, |
| /*origin_trial_token_b=*/kInvalidOriginToken); |
| |
| ResetURLAndAcceptLanguageSequence(); |
| // Accept-Language in the third request header is the same as the second one. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9,zh;q=0.8"}}, |
| "Verifying RestartOnB the third request sequence failed."); |
| |
| ResetURLAndAcceptLanguageSequence(); |
| // Fourth request will start to send the reduced Accept-Language header for B |
| // and it will do the language negotiation once the given origin opt-out |
| // the deprecation origin trial. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_a().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_b().spec(), "zh"}}, |
| "Verifying RestartOnB the fourth request sequence failed."); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(CrossOriginRedirectReduceAcceptLanguageOTBrowserTest, |
| RestartOnAB) { |
| // Restart on both A and B, and both origin opt-in the deprecation origin |
| // trial, then invalidate A's and B's token. Verify Accept-Language header in |
| // both A and B for the first two requests. |
| SetPrefsAcceptLanguage({"en-US", "zh"}); |
| SetOptions(/*avail_language_a=*/{"ja", "zh"}, |
| /*avail_language_b=*/{"de", "zh"}); |
| |
| // Set A opt-in and B opt-in the deprecation origin trial. |
| SetOriginTrialFirstPartyToken( |
| /*origin_trial_token_a=*/GetValidTokenA(), |
| /*origin_trial_token_b=*/GetValidTokenB()); |
| |
| ResetURLAndAcceptLanguageSequence(); |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| // 1. initial request to A with reduced accept-language of first language. |
| // 2. restart request to A with the persisted language zh. |
| // 3. initial request to B with reduced accept-language of first language. |
| // 4. restart request to A since redirect the original URL with persisted |
| // language zh. |
| // 5. restart request to B with the persisted language zh. |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_a().spec(), "zh"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_a().spec(), "zh"}, |
| {cross_origin_redirect_b().spec(), "zh"}}, |
| "Verifying the first request sequence failed."); |
| |
| ResetURLAndAcceptLanguageSequence(); |
| // Secondary redirect request expects no restarts and B starts to send |
| // unreduced accept-language. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "zh"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9,zh;q=0.8"}}, |
| "Verifying the second request sequence failed."); |
| |
| // Set A opt-out the deprecation origin trial. |
| SetOriginTrialFirstPartyToken(kInvalidOriginToken, GetValidTokenB()); |
| |
| ResetURLAndAcceptLanguageSequence(); |
| // For the first request after A opting-out the deprecation trial: there is no |
| // changes on both A and B. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "zh"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9,zh;q=0.8"}}, |
| "Verifying RestartOnAB the third request sequence failed."); |
| |
| ResetURLAndAcceptLanguageSequence(); |
| // For the second request after A opting-out the deprecation trial, it's the |
| // same as the above request. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "zh"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9,zh;q=0.8"}}, |
| "Verifying RestartOnAB the fourth request sequence failed."); |
| |
| // Set A and B both opt-out the deprecation origin trial. |
| SetOriginTrialFirstPartyToken(kInvalidOriginToken, kInvalidOriginToken); |
| |
| ResetURLAndAcceptLanguageSequence(); |
| // There is no change for the first request after B opting-out the |
| // deprecation trial. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "zh"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9,zh;q=0.8"}}, |
| "Verifying RestartOnAB the fifth request sequence failed."); |
| |
| ResetURLAndAcceptLanguageSequence(); |
| // For the second request after B opting-out the deprecation trial, there is |
| // no change on A, but B will do the language negotiation and send the new |
| // reduced Accept-Language. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), cross_origin_redirect_a())); |
| VerifyURLAndAcceptLanguageSequence( |
| {{cross_origin_redirect_a().spec(), "zh"}, |
| {cross_origin_redirect_b().spec(), "en-US,en;q=0.9"}, |
| {cross_origin_redirect_a().spec(), "zh"}, |
| {cross_origin_redirect_b().spec(), "zh"}}, |
| "Verifying RestartOnA the fourth request sequence failed."); |
| } |
| |
| // Browser tests verify same origin deprecation origin trial. |
| class SameOriginReduceAcceptLanguageDeprecationOTBrowserTest |
| : public ReduceAcceptLanguageBrowserTest { |
| public: |
| void VerifySubrequest( |
| const GURL& url, |
| const std::string& last_request_path, |
| const std::vector<std::string>& user_accept_languages, |
| int expect_restart_count, |
| int expect_fetch_count, |
| const std::optional<std::string>& expect_opt_in_fq_language, |
| const std::optional<std::string>& expect_opt_out_fq_language, |
| const std::string& expect_reduced_accept_language) { |
| base::HistogramTester histograms; |
| |
| // 1. Test opt-in deprecation origin trial. |
| SetOriginTrialFirstPartyToken(kValidFirstPartyToken); |
| // Verify the first request opt-in deprecation origin trial. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(url, |
| expect_opt_in_fq_language); |
| EXPECT_EQ(LastRequestUrl().path(), last_request_path); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure restart happen once. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, expect_restart_count); |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", |
| expect_fetch_count); |
| |
| // Verify navigator.languages returns full list of accept-languages if |
| // has valid deprecation origin trial token. |
| VerifyNavigatorLanguages(user_accept_languages); |
| |
| // Verify the send request after opt-in deprecation origin trial. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(url, std::nullopt); |
| |
| // 2. Test opt-out deprecation origin trial. |
| // Verify the first request has invalid deprecation origin trial token. |
| SetOriginTrialFirstPartyToken(kInvalidOriginToken); |
| NavigateAndVerifyAcceptLanguageOfLastRequest(url, |
| expect_opt_out_fq_language); |
| EXPECT_EQ(LastRequestUrl().path(), last_request_path); |
| VerifyNavigatorLanguages({user_accept_languages[0]}); |
| |
| // Verify the second request has invalid deprecation origin trial token, it |
| // should continue to reduce the Accept-Language. |
| NavigateAndVerifyAcceptLanguageOfLastRequest( |
| url, expect_reduced_accept_language); |
| EXPECT_EQ(LastRequestUrl().path(), last_request_path); |
| } |
| |
| void VerifySameOriginRequestNoRestart( |
| const std::optional<std::string>& expect_accept_language, |
| int expect_fetch_count, |
| int expect_store_count) { |
| base::HistogramTester histograms; |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), |
| expect_accept_language); |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure no restart happen. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 0); |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", |
| expect_fetch_count); |
| // Expect one storage update when response has a valid origin token. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", |
| expect_store_count); |
| } |
| |
| void VerifySameOriginTwoRequestsAfterTokenInvalid( |
| const std::optional<std::string>& expect_accept_language) { |
| SetOriginTrialFirstPartyToken(kInvalidOriginToken); |
| base::HistogramTester histograms; |
| // First request after token invalid will continue not sending reduced |
| // header since we can't verify the response header before preparing the |
| // request headers. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), |
| std::nullopt); |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| |
| // For opt-out deprecation origin trial, there is no clear cache operation. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.ClearLatency", 0); |
| |
| // The second request with invalid token should continue to reduced |
| // Accept-Language header. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), |
| expect_accept_language); |
| } |
| |
| protected: |
| void EnabledFeatures() override { |
| // Explicit enable feature ReduceAcceptLanguage. |
| scoped_feature_list_.InitAndEnableFeature( |
| {network::features::kReduceAcceptLanguage}); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(SameOriginReduceAcceptLanguageDeprecationOTBrowserTest, |
| FirstRequestMatchPrimaryLanguage) { |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| SetOriginTrialFirstPartyToken(kValidFirstPartyToken); |
| SetPrefsAcceptLanguage({"es", "zh"}); |
| |
| // The first request will add the reduced Accept-Language in navigation |
| // request since we enabled the ReduceAcceptLanguage. |
| // One fetch for initially checking whether need to add reduce Accept-Language |
| // header. No call to store the language since we don't store the reduced |
| // language which is the first primary language. |
| VerifySameOriginRequestNoRestart(/*expect_accept_language=*/"es", |
| /*expect_fetch_count=*/1, |
| /*expect_store_count=*/0); |
| // The second request should not send out reduced Accept-Language, the network |
| // layer will add the full Accept-Language list. |
| VerifySameOriginRequestNoRestart(/*expect_accept_language=*/std::nullopt, |
| /*expect_fetch_count=*/0, |
| /*expect_store_count=*/0); |
| // Verify requests after invalid token will continue send the reduced |
| // accept-language. |
| VerifySameOriginTwoRequestsAfterTokenInvalid("es"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SameOriginReduceAcceptLanguageDeprecationOTBrowserTest, |
| FirstRequestMatchNonPrimaryLanguage) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "en-US, es;d", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| SetOriginTrialFirstPartyToken(kValidFirstPartyToken); |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // First request restarts and send Accept-Language with negotiated language: |
| // en-us, the deprecation origin trial doesn't apply to the first request. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), |
| "en-US,en;q=0.9"); |
| // Ensure only restart once. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 1); |
| |
| // Two fetches for initially adding header and restart fetch. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 2); |
| // Expect no perf storage updates. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 1); |
| |
| // The second request shouldn't send out any reduced accept-language due to |
| // opted-in the deprecation origin trial. |
| VerifySameOriginRequestNoRestart(/*expect_accept_language=*/std::nullopt, |
| /*expect_fetch_count=*/0, |
| /*expect_store_count=*/0); |
| // Verify requests after invalid token will continue send the reduced |
| // accept-language. |
| VerifySameOriginTwoRequestsAfterTokenInvalid("en-US,en;q=0.9"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SameOriginReduceAcceptLanguageDeprecationOTBrowserTest, |
| FirstRequestMatchNonPrimaryLanguageWithCriticalTrial) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "en-US, es;d", |
| .vary_in_parent = "accept-language", |
| .is_critical_origin_trial = true}, |
| {SameOriginRequestUrl()}); |
| SetOriginTrialFirstPartyToken(kValidFirstPartyToken); |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // First request restarts and won't send reduced Accept-Language header due to |
| // critical deprecation origin trial apply to the first request. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), |
| std::nullopt); |
| // Ensure only restart once. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 1); |
| |
| // The second request shouldn't send out any reduced accept-language due to |
| // opted-in the deprecation origin trial. |
| VerifySameOriginRequestNoRestart(/*expect_accept_language=*/std::nullopt, |
| /*expect_fetch_count=*/0, |
| /*expect_store_count=*/0); |
| // Verify requests after invalid token will continue to send the reduced |
| // accept-language. |
| VerifySameOriginTwoRequestsAfterTokenInvalid("en-US,en;q=0.9"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SameOriginReduceAcceptLanguageDeprecationOTBrowserTest, |
| FirstRequestNoMatchLanguage) { |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| SetOriginTrialFirstPartyToken(kValidFirstPartyToken); |
| SetPrefsAcceptLanguage({"zh", "ja"}); |
| |
| // The first request adds the reduced Accept-Language in navigation request |
| // since we enabled ReduceAcceptLanguage feature. |
| VerifySameOriginRequestNoRestart(/*expect_accept_language=*/"zh", |
| /*expect_fetch_count=*/1, |
| /*expect_store_count=*/0); |
| // The second request should not send out reduced Accept-Language, the network |
| // layer will add the full Accept-Language list. |
| VerifySameOriginRequestNoRestart(/*expect_accept_language=*/std::nullopt, |
| /*expect_fetch_count=*/0, |
| /*expect_store_count=*/0); |
| // Verify requests after invalid token will continue send the reduced |
| // accept-language. |
| VerifySameOriginTwoRequestsAfterTokenInvalid("zh"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SameOriginReduceAcceptLanguageDeprecationOTBrowserTest, |
| IframeRequestRestart) { |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "es", |
| .avail_language_in_child = "es, en-US", |
| .vary_in_child = "accept-language", |
| .is_critical_origin_trial = false}, |
| {SameOriginIframeUrl(), SimpleRequestUrl()}); |
| std::vector<std::string> user_accept_languages = {"zh", "en-US"}; |
| SetPrefsAcceptLanguage(user_accept_languages); |
| |
| // Total two different url requests: |
| // * same_origin_iframe_url: one fetch for initially adding header and another |
| // one for the restart request adding header. |
| // * simple_request_url: no fetch due to opted-in deprecation origin trial. |
| // |
| // For the first iframe request after site opt-in the deprecation OT, we |
| // won't add the Accept-Language since language negotiation restart the |
| // request and find a valid deprecation origin trial token. Network layer |
| // will add the full list of user's Accept-Language. |
| // For the first iframe request after site opt-out (or invalid token) the |
| // deprecation OT, we resume to reduce the Accept-Language, however, the |
| // reduced accept-language is the first user accept-language because the |
| // negotiated language won't take effect for the first request after opt-out |
| // the deprecation trial without the critical trial. |
| VerifySubrequest( |
| /*url=*/SameOriginIframeUrl(), |
| /*last_request_path=*/"/subframe_simple.html", |
| /*user_accept_languages=*/user_accept_languages, |
| /*expect_restart_count=*/1, |
| /*expect_fetch_count=*/2, |
| /*expect_opt_in_fq_language=*/std::nullopt, |
| /*expect_opt_out_fq_language=*/"zh", |
| /*expect_reduced_accept_language=*/"en-US,en;q=0.9"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SameOriginReduceAcceptLanguageDeprecationOTBrowserTest, |
| IframeRequestRestartWithCriticalTrial) { |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "es", |
| .avail_language_in_child = "es, en-US", |
| .vary_in_child = "accept-language", |
| .is_critical_origin_trial = true}, |
| {SameOriginIframeUrl(), SimpleRequestUrl()}); |
| std::vector<std::string> user_accept_languages = {"zh", "en-US"}; |
| SetPrefsAcceptLanguage(user_accept_languages); |
| |
| // Only one fetch reduced Accept-Language prefs for initially add the |
| // Accept-Language HTTP header, no fetch call for restart request since we |
| // added the deprecation trial as critical trial. Also, we add the critical |
| // trial, the negotiated language should take effect for the first request |
| // after opt-out the deprecation trial. |
| VerifySubrequest( |
| /*url=*/SameOriginIframeUrl(), |
| /*last_request_path=*/"/subframe_simple.html", |
| /*user_accept_languages=*/user_accept_languages, |
| /*expect_restart_count=*/1, |
| /*expect_fetch_count=*/1, |
| /*expect_opt_in_fq_language=*/std::nullopt, |
| /*expect_opt_out_fq_language=*/"en-US,en;q=0.9", |
| /*expect_reduced_accept_language=*/"en-US,en;q=0.9"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SameOriginReduceAcceptLanguageDeprecationOTBrowserTest, |
| IframeRequestNoRestart) { |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .is_critical_origin_trial = false}, |
| {{SameOriginIframeUrl(), SimpleRequestUrl()}}); |
| std::vector<std::string> user_accept_languages = {"es", "ja"}; |
| SetPrefsAcceptLanguage(user_accept_languages); |
| |
| // No restart |
| VerifySubrequest( |
| /*url=*/SameOriginIframeUrl(), |
| /*last_request_path=*/"/subframe_simple.html", |
| /*user_accept_languages=*/user_accept_languages, |
| /*expect_restart_count=*/0, |
| /*expect_fetch_count=*/1, |
| /*expect_opt_in_fq_language=*/std::nullopt, |
| /*expect_opt_out_fq_language=*/"es", |
| /*expect_reduced_accept_language=*/"es"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SameOriginReduceAcceptLanguageDeprecationOTBrowserTest, |
| IframeRequestNoRestartWithCriticalTrial) { |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .is_critical_origin_trial = true}, |
| {{SameOriginIframeUrl(), SimpleRequestUrl()}}); |
| std::vector<std::string> user_accept_languages = {"es", "ja"}; |
| SetPrefsAcceptLanguage(user_accept_languages); |
| |
| // No restart |
| VerifySubrequest( |
| /*url=*/SameOriginIframeUrl(), |
| /*last_request_path=*/"/subframe_simple.html", |
| /*user_accept_languages=*/user_accept_languages, |
| /*expect_restart_count=*/0, |
| /*expect_fetch_count=*/1, |
| /*expect_opt_in_fq_language=*/std::nullopt, |
| /*expect_opt_out_fq_language=*/"es", |
| /*expect_reduced_accept_language=*/"es"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SameOriginReduceAcceptLanguageDeprecationOTBrowserTest, |
| ImgSubresourceRestart) { |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "es", |
| .avail_language_in_child = "es, en-US", |
| .vary_in_child = "accept-language", |
| .is_critical_origin_trial = false}, |
| {SameOriginImgUrl(), SimpleImgUrl()}); |
| std::vector<std::string> user_accept_languages = {"zh", "en-US"}; |
| SetPrefsAcceptLanguage(user_accept_languages); |
| |
| // Total two different url requests: |
| // * same_origin_image_url: one fetch for initially adding header and another |
| // one for the restart request adding header. |
| // * subresource_simple.jpg: no language fetch on subresource. |
| // |
| // For the first subresource request after site opt-in the deprecation OT. |
| // We won't add the Accept-Language since language negotiation restart the |
| // request and find a valid deprecation origin trial token. Network layer |
| // will add the full list of user's Accept-Language. |
| // For the first subresource request after site opt-out (or invalid token) |
| // the deprecation OT, we also won't add the Accept-Language since we cleared |
| // the commit language for subresource once we see invalid OT token when |
| // committing the navigation. |
| VerifySubrequest( |
| /*url=*/SameOriginImgUrl(), |
| /*last_request_path=*/"/subresource_simple.jpg", |
| /*user_accept_languages=*/user_accept_languages, |
| /*expect_restart_count=*/1, |
| /*expect_fetch_count=*/2, |
| /*expect_opt_in_fq_language=*/std::nullopt, |
| /*expect_opt_out_fq_language=*/std::nullopt, |
| /*expect_reduced_accept_language=*/"en-US,en;q=0.9"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SameOriginReduceAcceptLanguageDeprecationOTBrowserTest, |
| ImgSubresourceRestartWithCriticalTrial) { |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "es", |
| .avail_language_in_child = "es, en-US", |
| .vary_in_child = "accept-language", |
| .is_critical_origin_trial = true}, |
| {SameOriginImgUrl(), SimpleImgUrl()}); |
| std::vector<std::string> user_accept_languages = {"zh", "en-US"}; |
| SetPrefsAcceptLanguage(user_accept_languages); |
| |
| // Only one fetch call for initially add the reduced Accept-Language header, |
| // no fetch call for restart request since we added the deprecation trial as |
| // critical trial. |
| VerifySubrequest( |
| /*url=*/SameOriginImgUrl(), |
| /*last_request_path=*/"/subresource_simple.jpg", |
| /*user_accept_languages=*/user_accept_languages, |
| /*expect_restart_count=*/1, |
| /*expect_fetch_count=*/1, |
| /*expect_opt_in_fq_language=*/std::nullopt, |
| /*expect_opt_out_fq_language=*/std::nullopt, |
| /*expect_reduced_accept_language=*/"en-US,en;q=0.9"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SameOriginReduceAcceptLanguageDeprecationOTBrowserTest, |
| ImgSubresourceNoRestart) { |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language"}, |
| {{SameOriginImgUrl(), SimpleImgUrl()}}); |
| std::vector<std::string> user_accept_languages = {"es", "ja"}; |
| SetPrefsAcceptLanguage(user_accept_languages); |
| |
| // No restart |
| VerifySubrequest( |
| /*url=*/SameOriginImgUrl(), |
| /*last_request_path=*/"/subresource_simple.jpg", |
| /*user_accept_languages=*/user_accept_languages, |
| /*expect_restart_count=*/0, |
| /*expect_fetch_count=*/1, |
| /*expect_opt_in_fq_language=*/std::nullopt, |
| /*expect_opt_out_fq_language=*/std::nullopt, |
| /*expect_reduced_accept_language=*/"es"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SameOriginReduceAcceptLanguageDeprecationOTBrowserTest, |
| ImgSubresourceNoRestartWithCriticalTrial) { |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .is_critical_origin_trial = true}, |
| {{SameOriginImgUrl(), SimpleImgUrl()}}); |
| std::vector<std::string> user_accept_languages = {"es", "ja"}; |
| SetPrefsAcceptLanguage(user_accept_languages); |
| |
| // No restart |
| VerifySubrequest( |
| /*url=*/SameOriginImgUrl(), |
| /*last_request_path=*/"/subresource_simple.jpg", |
| /*user_accept_languages=*/user_accept_languages, |
| /*expect_restart_count=*/0, |
| /*expect_fetch_count=*/1, |
| /*expect_opt_in_fq_language=*/std::nullopt, |
| /*expect_opt_out_fq_language=*/std::nullopt, |
| /*expect_reduced_accept_language=*/"es"); |
| } |
| |
| // Browser tests verify third party deprecation origin trial. |
| class ThirdPartyReduceAcceptLanguageDeprecationOTBrowserTest |
| : public ThirdPartyReduceAcceptLanguageBrowserTest { |
| protected: |
| void EnabledFeatures() override { |
| // Explicit enable feature ReduceAcceptLanguage ReduceAcceptLanguage. |
| scoped_feature_list_.InitWithFeatures( |
| {network::features::kReduceAcceptLanguage}, {}); |
| } |
| }; |
| |
| // For third-party embedded as an iframe, the third-party can opt-in the |
| // deprecation trial as a first party deprecation origin trial. |
| IN_PROC_BROWSER_TEST_F(ThirdPartyReduceAcceptLanguageDeprecationOTBrowserTest, |
| IframeRequests) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "zh", |
| .avail_language_in_child = "zh", |
| .vary_in_child = "accept-language"}, |
| {CrossOriginIframeUrl(), SimpleThirdPartyRequestUrl()}); |
| |
| SetOriginTrialThirdPartyToken(kValidMySiteFirstPartyToken); |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // The first third-party iframe subrequest expect continue to send reduced |
| // Accept-Language which is inherited from the top-level frame request. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(CrossOriginIframeUrl(), |
| "en-US,en;q=0.9"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure only one restart to do the language negotiation. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 1); |
| // cross_origin_iframe_url: one fetch for initially adding header and |
| // another one for the restart request adding header. |
| // simple_3p_request_url: one fetch for initially adding header. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 3); |
| // Persist reduce accept language happens. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 1); |
| |
| EXPECT_EQ(LastRequestUrl().path(), "/subframe_simple_3p.html"); |
| |
| // For the second request, we expect no reduced Accept-Language send once |
| // the deprecation origin trial takes effect. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(CrossOriginIframeUrl(), |
| std::nullopt); |
| |
| // For the first request we continue to send the reduced Accept-Language since |
| // we persist the deprecation origin trial token on a third-party context |
| // which means the partition origin is first part origin. |
| // For the second request, we won't send reduce Accept-Language as deprecation |
| // origin trial take effects, in this case, the partition origin is the |
| // third-party origin itself. |
| base::HistogramTester histograms2; |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SimpleThirdPartyRequestUrl(), |
| "zh"); |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SimpleThirdPartyRequestUrl(), |
| std::nullopt); |
| histograms2.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 0); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ThirdPartyReduceAcceptLanguageDeprecationOTBrowserTest, |
| JavaScriptRequest) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions( |
| {.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "zh", |
| .avail_language_in_child = "zh", |
| .vary_in_child = "accept-language"}, |
| {CrossOriginSubresourceUrl(), CrossOriginMetaTagInjectingJavascriptUrl(), |
| CrossOriginCssRequestUrl()}); |
| |
| SetOriginTrialThirdPartyToken(kValidThirdPartyToken); |
| SetPrefsAcceptLanguage({"zh", "en-US"}); |
| |
| // Third party iframe subrequest expect inherit the reduced Accept-Language |
| // from the top-level navigation requests. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(CrossOriginSubresourceUrl(), |
| "zh"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Ensure no restart happen. |
| histograms.ExpectBucketCount( |
| "ReduceAcceptLanguage.AcceptLanguageNegotiationRestart", |
| /*=kNavigationRestarted=*/3, 0); |
| // One fetch for initially checking whether need to add reduce Accept-Language |
| // header to the top-level frame. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.FetchLatencyUs", 1); |
| // No persist reduce accept language happens. |
| histograms.ExpectTotalCount("ReduceAcceptLanguage.StoreLatency", 0); |
| |
| // All subresources should have been loaded, |
| EXPECT_THAT(intercepted_load_urls_, |
| testing::IsSupersetOf({CrossOriginMetaTagInjectingJavascriptUrl(), |
| CrossOriginCssRequestUrl()})); |
| |
| // Ensure third-party JavaScript access JS getters get the full list of |
| // accept-language. |
| VerifyNavigatorLanguages({"zh", "en-US"}); |
| } |
| |
| // Browser tests verify reduce the total number of Accept-Language. |
| class ReduceAcceptLanguageCountBrowserTest |
| : public ReduceAcceptLanguageBrowserTest, |
| public testing::WithParamInterface<bool> { |
| protected: |
| void EnabledFeatures() override { |
| // If explicitly set custom count set 5 as max. |
| if (GetParam()) { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| /*enabled_features=*/{{network::features::kReduceAcceptLanguageCount, |
| {{network::features::kMaxAcceptLanguage.name, |
| "5"}}}}, |
| /*disabled_features=*/{network::features::kReduceAcceptLanguage, |
| network::features::kReduceAcceptLanguageHTTP}); |
| } else { |
| scoped_feature_list_.InitWithFeatures( |
| /*enabled_features=*/{network::features::kReduceAcceptLanguageCount}, |
| /*disabled_features=*/{network::features::kReduceAcceptLanguage, |
| network::features::kReduceAcceptLanguageHTTP}); |
| } |
| } |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(FeatureFlag, |
| ReduceAcceptLanguageCountBrowserTest, |
| testing::Values(true, false)); |
| |
| IN_PROC_BROWSER_TEST_P(ReduceAcceptLanguageCountBrowserTest, RegularRequest) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "en", |
| .avail_language_in_parent = "en, en-US", |
| .vary_in_parent = "accept-language"}, |
| {SameOriginRequestUrl()}); |
| SetPrefsAcceptLanguage(base::SplitString( |
| kLargeLanguages, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)); |
| |
| // Expect no Accept-Language header added because browser_tests can only check |
| // headers in navigation layer, browser_tests can't see headers added by |
| // network stack. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginRequestUrl(), |
| std::nullopt); |
| if (GetParam()) { |
| VerifyNavigatorLanguages({"zh", "zh-CN", "en-US", "en", "af"}); |
| } else { |
| VerifyNavigatorLanguages( |
| {"zh", "zh-CN", "en-US", "en", "af", "sq", "am", "ar", "an", "hy"}); |
| } |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Expect a total count of 3. The histogram is recorded once during initial |
| // profile setup, and then twice more when SetPrefsAcceptLanguage is called |
| // to sync the preference to the renderer and network services. |
| histograms.ExpectTotalCount("LanguageUsage.AcceptLanguage.Count2", 3); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(ReduceAcceptLanguageCountBrowserTest, Iframe) { |
| base::HistogramTester histograms; |
| |
| SetTestOptions({.content_language_in_parent = "es", |
| .avail_language_in_parent = "es, en-US", |
| .vary_in_parent = "accept-language", |
| .content_language_in_child = "es", |
| .avail_language_in_child = "es, en-US", |
| .vary_in_child = "accept-language"}, |
| {SameOriginIframeUrl(), SimpleRequestUrl()}); |
| |
| SetPrefsAcceptLanguage(base::SplitString( |
| kLargeLanguages, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)); |
| |
| // Expect no Accept-Language header added because browser_tests can only check |
| // headers in navigation layer, browser_tests can't see headers added by |
| // network stack. |
| NavigateAndVerifyAcceptLanguageOfLastRequest(SameOriginIframeUrl(), |
| std::nullopt); |
| if (GetParam()) { |
| VerifyNavigatorLanguages({"zh", "zh-CN", "en-US", "en", "af"}); |
| } else { |
| VerifyNavigatorLanguages( |
| {"zh", "zh-CN", "en-US", "en", "af", "sq", "am", "ar", "an", "hy"}); |
| } |
| EXPECT_EQ(LastRequestUrl().path(), "/subframe_simple.html"); |
| |
| metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| // Expect a total count of 3. The histogram is recorded once during initial |
| // profile setup, and then twice more when SetPrefsAcceptLanguage is called |
| // to sync the preference to the renderer and network services. |
| histograms.ExpectTotalCount("LanguageUsage.AcceptLanguage.Count2", 3); |
| } |