Reland "Add Sec-CH-UA-Reduced client hint header when Origin Trial token present"

This reverts commit f6370c35ee4f448247485c6e875d3c60679d0454.

Reason for revert: The original CL (crrev.com/c/3042810) started the EmbeddedTestServer on a specific port in the UaReducedOriginTrialBrowserTest, since the test used an Origin Trial token that is bound to a specific origin.  This caused the test to fail on CI for linux-chromeos-chrome, due to being unable to start EmbeddedTestServer as the specific port was already in-use, and the original CL was reverted in crrev.com/c/3061119.  This CL relands the original CL and fixes the problem by using URLLoaderInterceptor instead of EmbeddedTestServer in the UaReducedOriginTrialBrowserTest tests.

Original change's description:
> Revert "Add Sec-CH-UA-Reduced client hint header when Origin Trial token present"
>
> This reverts commit d9c057ea37355847f0c9530eada84aca335c96b4.
>
> Reason for revert: Browser tests is failing on linux-chromeos-chrome.
> See [1].
>
> [1] https://ci.chromium.org/ui/p/chrome/builders/ci/linux-chromeos-chrome/16242/overview
>
> Original change's description:
> > Add Sec-CH-UA-Reduced client hint header when Origin Trial token present
> >
> > In this CL, we add logic to parse and accept the Sec-CH-UA-Reduced
> > client hint in the Accept-CH header and and only send the
> > Sec-CH-UA-Reduced client hint only in the presence of a valid
> > UserAgentReduction Origin Trial token.
> >
> > If the Origin Trial token is present and valid, and the origin
> > contains Sec-CH-UA-Reduced in the Accept-CH cache, then on all
> > subsequent requests to the origin, the Sec-CH-UA-Reduced request
> > header will be set with a value of "?1" (sh-boolean).  If the
> > Accept-CH cache does not contain Sec-CH-UA-Reduced, or the Origin
> > Trial token is not valid, then Sec-CH-UA-Reduced will not be sent
> > in the request headers.
> >
> > NB: A subsequent CL will change the User-Agent request header to
> > the reduced UA string if the Sec-CH-UA-Reduced header is sent.
> >
> > Chrome Platform Status: https://chromestatus.com/feature/5704553745874944
> > Design Doc: https://docs.google.com/document/d/1feIxK9S7oNgT2oGGebbxE9X0O-4wTKcsP_gRaY99tq4
> >
> > Bug: 1222742
> > Change-Id: If855d4bb393540d49de3be80aa9bc7c80f861c50
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3042810
> > Commit-Queue: Ali Beyad <[email protected]>
> > Reviewed-by: Aaron Tagliaboschi <[email protected]>
> > Reviewed-by: Robert Kroeger <[email protected]>
> > Reviewed-by: Mike West <[email protected]>
> > Cr-Commit-Position: refs/heads/master@{#906810}
>
> Bug: 1222742
> Change-Id: I0d8aab1d56d78f8323d4a3e3ed88b6eef370e1b9
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3061119
> Auto-Submit: Daniel Hosseinian <[email protected]>
> Commit-Queue: Rubber Stamper <[email protected]>
> Bot-Commit: Rubber Stamper <[email protected]>
> Owners-Override: Daniel Hosseinian <[email protected]>
> Cr-Commit-Position: refs/heads/master@{#906884}

Bug: 1222742
Change-Id: I3ed39697255116fe1f2ab5203331e8786e9d66a3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3064532
Commit-Queue: Ali Beyad <[email protected]>
Reviewed-by: Kinuko Yasuda <[email protected]>
Reviewed-by: Aaron Tagliaboschi <[email protected]>
Reviewed-by: Daniel Hosseinian <[email protected]>
Cr-Commit-Position: refs/heads/master@{#909169}
diff --git a/chrome/browser/client_hints/client_hints_browsertest.cc b/chrome/browser/client_hints/client_hints_browsertest.cc
index c44f7f87..5f3ef9fd 100644
--- a/chrome/browser/client_hints/client_hints_browsertest.cc
+++ b/chrome/browser/client_hints/client_hints_browsertest.cc
@@ -11,8 +11,10 @@
 #include "base/containers/contains.h"
 #include "base/metrics/field_trial_param_associator.h"
 #include "base/run_loop.h"
+#include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "base/synchronization/lock.h"
+#include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "build/build_config.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
@@ -28,17 +30,22 @@
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/pref_names.h"
+#include "components/embedder_support/switches.h"
 #include "components/embedder_support/user_agent_utils.h"
 #include "components/metrics/content/subprocess_metrics_provider.h"
 #include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/test_utils.h"
 #include "content/public/test/url_loader_interceptor.h"
 #include "net/dns/mock_host_resolver.h"
@@ -51,8 +58,11 @@
 #include "services/network/public/cpp/cors/cors.h"
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/network_switches.h"
+#include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/mojom/web_client_hints_types.mojom-shared.h"
+#include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/client_hints/client_hints.h"
 #include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
@@ -692,8 +702,10 @@
         continue;
       }
 
-      // TODO(crbug.com/1226193): Remove this block when support for
-      // `Sec-CH-UA-Reduced` has been added.
+      // Skip over the `Sec-CH-UA-Reduced` client hint because it is only added
+      // in the presence of a valid "UserAgentReduction" Origin Trial token.
+      // `Sec-CH-UA-Reduced` is tested via UaReducedOriginTrialBrowserTest
+      // below.
       if (std::string(blink::kClientHintsHeaderMapping[i]) ==
           "sec-ch-ua-reduced") {
         continue;
@@ -2475,3 +2487,184 @@
   ui_test_utils::NavigateToURL(browser(), test_url());
   EXPECT_EQ(prefers_color_scheme_observed(), "dark");
 }
+
+// Tests that the Sec-CH-UA-Reduced client hint is sent if and only if the
+// UserAgentReduction Origin Trial token is present and valid in the response
+// headers.
+//
+// The test Origin Trial token found in the test files was generated by running
+// (in tools/origin_trials):
+// generate_token.py https://127.0.0.1:44444 UserAgentReduction
+// --expire-timestamp=2000000000
+//
+// The Origin Trial token expires in 2033.  Generate a new token by then, or
+// find a better way to re-generate a test trial token.
+class UaReducedOriginTrialBrowserTest : public InProcessBrowserTest {
+ public:
+  UaReducedOriginTrialBrowserTest() = default;
+
+  // The URL that was used to register for the Origin Trial token.
+  static constexpr const char kOriginUrl[] = "https://127.0.0.1:44444";
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // The public key for the default privatey 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 SetUp() override {
+    std::unique_ptr<base::FeatureList> feature_list =
+        std::make_unique<base::FeatureList>();
+    feature_list->InitializeFromCommandLine("CriticalClientHint", "");
+    scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
+
+    InProcessBrowserTest::SetUp();
+  }
+
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+
+    // We use a URLLoaderInterceptor, rather than the EmbeddedTestServer, since
+    // the origin trial token in the response is associated with a fixed
+    // origin, whereas EmbeddedTestServer serves content on a random port.
+    url_loader_interceptor_ =
+        content::URLLoaderInterceptor::ServeFilesFromDirectoryAtOrigin(
+            "chrome/test/data/client_hints", GURL(kOriginUrl));
+  }
+
+  void TearDownOnMainThread() override {
+    url_loader_interceptor_.reset();
+    InProcessBrowserTest::TearDownOnMainThread();
+  }
+
+  GURL ua_reduced_with_valid_origin_trial_token_url() const {
+    return GURL(base::StrCat(
+        {kOriginUrl, "/accept_ch_ua_reduced_with_valid_origin_trial.html"}));
+  }
+
+  GURL ua_reduced_with_invalid_origin_trial_token_url() const {
+    return GURL(base::StrCat(
+        {kOriginUrl, "/accept_ch_ua_reduced_with_invalid_origin_trial.html"}));
+  }
+
+  GURL ua_reduced_with_no_origin_trial_token_url() const {
+    return GURL(base::StrCat(
+        {kOriginUrl, "/accept_ch_ua_reduced_with_no_origin_trial.html"}));
+  }
+
+  GURL ua_reduced_missing_with_valid_origin_trial_token_url() const {
+    return GURL(base::StrCat(
+        {kOriginUrl, "/accept_ch_ua_reduced_missing_valid_origin_trial.html"}));
+  }
+
+  GURL critical_ch_ua_reduced_with_valid_origin_trial_token_url() const {
+    return GURL(base::StrCat(
+        {kOriginUrl, "/critical_ch_ua_reduced_with_valid_origin_trial.html"}));
+  }
+
+  GURL critical_ch_ua_reduced_with_invalid_origin_trial_token_url() const {
+    return GURL(base::StrCat(
+        {kOriginUrl,
+         "/critical_ch_ua_reduced_with_invalid_origin_trial.html"}));
+  }
+
+  void NavigateAndCheckHeader(const GURL& url,
+                              const bool ch_ua_reduced_expected) {
+    ui_test_utils::NavigateToURL(browser(), url);
+    base::RunLoop().RunUntilIdle();
+
+    std::string header_value;
+    const bool ch_ua_reduced =
+        url_loader_interceptor_->GetLastRequestHeaders().GetHeader(
+            "sec-ch-ua-reduced", &header_value);
+
+    EXPECT_EQ(ch_ua_reduced, ch_ua_reduced_expected);
+    if (ch_ua_reduced_expected) {
+      EXPECT_EQ(header_value, "?1");
+    }
+  }
+
+  void NavigateTwiceAndCheckHeader(const GURL& url,
+                                   const bool ch_ua_reduced_expected,
+                                   const bool critical_ch_ua_reduced_expected) {
+    // If Critical-CH is set, we expect Sec-CH-UA-Reduced in the first
+    // navigation request header.  If Critical-CH is not set, we don't expect
+    // Sec-CH-UA-Reduced in the first navigation request.
+    NavigateAndCheckHeader(
+        url, critical_ch_ua_reduced_expected && ch_ua_reduced_expected);
+
+    // Regardless of the Critical-CH setting, we expect the Sec-CH-UA-Reduced
+    // client hint sent on the second request, if Sec-CH-UA-Reduced is set and
+    // the Origin Trial token is valid.
+    NavigateAndCheckHeader(url, ch_ua_reduced_expected);
+  }
+
+ private:
+  std::unique_ptr<content::URLLoaderInterceptor> url_loader_interceptor_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+constexpr const char UaReducedOriginTrialBrowserTest::kOriginUrl[];
+
+IN_PROC_BROWSER_TEST_F(UaReducedOriginTrialBrowserTest,
+                       AcceptChUaReducedWithValidOriginTrialToken) {
+  NavigateTwiceAndCheckHeader(ua_reduced_with_valid_origin_trial_token_url(),
+                              /*ch_ua_reduced_expected=*/true,
+                              /*critical_ch_ua_reduced_expected=*/false);
+}
+
+IN_PROC_BROWSER_TEST_F(UaReducedOriginTrialBrowserTest,
+                       AcceptChUaReducedWithInvalidOriginTrialToken) {
+  // The response contained Sec-CH-UA-Reduced in the Accept-CH header, but the
+  // origin trial token is invalid.
+  NavigateTwiceAndCheckHeader(ua_reduced_with_invalid_origin_trial_token_url(),
+                              /*ch_ua_reduced_expected=*/false,
+                              /*critical_ch_ua_reduced_expected=*/false);
+}
+
+IN_PROC_BROWSER_TEST_F(UaReducedOriginTrialBrowserTest,
+                       AcceptChUaReducedWithNoOriginTrialToken) {
+  // The response contained Sec-CH-UA-Reduced in the Accept-CH header, but the
+  // origin trial token is not present.
+  NavigateTwiceAndCheckHeader(ua_reduced_with_no_origin_trial_token_url(),
+                              /*ch_ua_reduced_expected=*/false,
+                              /*critical_ch_ua_reduced_expected=*/false);
+}
+
+IN_PROC_BROWSER_TEST_F(UaReducedOriginTrialBrowserTest,
+                       NoAcceptChUaReducedWithValidOriginTrialToken) {
+  // The response contained a valid Origin Trial token, but no Sec-CH-UA-Reduced
+  // in the Accept-CH header.
+  NavigateTwiceAndCheckHeader(
+      ua_reduced_missing_with_valid_origin_trial_token_url(),
+      /*ch_ua_reduced_expected=*/false,
+      /*critical_ch_ua_reduced_expected=*/false);
+}
+
+IN_PROC_BROWSER_TEST_F(UaReducedOriginTrialBrowserTest,
+                       CriticalChUaReducedWithValidOriginTrialToken) {
+  // The initial navigation also contains the Critical-CH header, so the
+  // Sec-CH-UA-Reduced header should be set after the first navigation.
+  NavigateTwiceAndCheckHeader(
+      critical_ch_ua_reduced_with_valid_origin_trial_token_url(),
+      /*ch_ua_reduced_expected=*/true,
+      /*critical_ch_ua_reduced_expected=*/true);
+}
+
+IN_PROC_BROWSER_TEST_F(UaReducedOriginTrialBrowserTest,
+                       CriticalChUaReducedWithInvalidOriginTrialToken) {
+  // The Origin Trial token is invalid, so the Critical-CH should not have
+  // resulted in the Sec-CH-UA-Reduced header being sent.
+  NavigateTwiceAndCheckHeader(
+      critical_ch_ua_reduced_with_invalid_origin_trial_token_url(),
+      /*ch_ua_reduced_expected=*/false,
+      /*critical_ch_ua_reduced_expected=*/false);
+}
+
+IN_PROC_BROWSER_TEST_F(UaReducedOriginTrialBrowserTest,
+                       ThirdPartyUaReducedWithValidOriginTrialToken) {
+  // TODO(crbug.com/1222742): Implement this test.
+}