diff --git a/.gn b/.gn
index c977986..fd9d87b 100644
--- a/.gn
+++ b/.gn
@@ -67,7 +67,7 @@
   "//extensions:*",  # 28 errors
   "//headless:*",  # 167 errors
   "//ppapi/proxy:ipc_sources",  # 13 errors
-  "//ppapi/proxy:proxy",  # 5 errors
+  "//ppapi/proxy:proxy",  # 4 errors
   "//ppapi/thunk:*",  # 1071 errors
   "//remoting/host/security_key:*",  # 10 errors
   "//remoting/host/win:*",  # 43 errors
diff --git a/DEPS b/DEPS
index 7cb5e9694..fc560ba 100644
--- a/DEPS
+++ b/DEPS
@@ -190,7 +190,7 @@
   # luci-go CIPD package version.
   # Make sure the revision is uploaded by infra-packagers builder.
   # https://ci.chromium.org/p/infra-internal/g/infra-packagers/console
-  'luci_go': 'git_revision:6808332cfd84a07aeefa906674273fc762510c8c',
+  'luci_go': 'git_revision:2f836b4882d2fa8c7a44c8ac8881c3a17fad6a86',
 
   # This can be overridden, e.g. with custom_vars, to build clang from HEAD
   # instead of downloading the prebuilt pinned revision.
@@ -222,11 +222,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '50f0dadb7fc864de7baa31946eb83c03e1d68bdc',
+  'skia_revision': '6de2e1db031134a85a98e405658ded37d7e1aa70',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '2bc9e275213af0df521c4ab3d8cd1bc16780de9c',
+  'v8_revision': 'bdbacd5ac30b8aebad7bea639b16cc4ddf456cf5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -234,15 +234,15 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '17a4b6e7f6bd329953a46f9b2050670b0f27bfd0',
+  'angle_revision': '8d0eb08c46b037d1a020a3ce5faeff05355f8754',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '02834d41a934f5707300a9f15f83214169242369',
+  'swiftshader_revision': '4716eec90fcbb105b4694807121c7a57d7cf8fd9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '8496d13f2daade3d96291c19a0fcfc408fcd128d',
+  'pdfium_revision': 'fa49ce808596a75499e2f63ad0a4ac225679c2a8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -301,7 +301,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '9d5b8f2d3bdce7f2fa15962c1d1f272a7b77dfb5',
+  'devtools_frontend_revision': 'd2a1805902840e121673bccff55f019fd9376dc5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -345,7 +345,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': '3290ea5420a7a49a8a37e7ffb957fea7b81676cb',
+  'quiche_revision': '51f584db29001036c20db3f72f09b00b875ae625',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -581,7 +581,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'afd83e4c9037967079201edd6e934bbd66cfa6b5',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '9e15d4d1ad54c5d5ca2048b4f2b0fe490c51a7ff',
       'condition': 'checkout_ios',
   },
 
@@ -1343,7 +1343,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '3dd5b80bc4f172dd82925bb259cb7c82348409c5',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + '6c8b744d97aa6c8c7de0f9a3e3098d09e833c2e2',
+    Var('chromium_git') + '/openscreen' + '@' + 'c493f7233e12569bbd6186c387b132e274a27d51',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + 'bf21ccb1007bb531b45d9978919a56ea5059c245',
@@ -1360,7 +1360,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '6908f0ef6c79e5ecacd7ba4e8e2d001cd709a302',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '71d582b360331087630937eaec1d194f0c0b78fb',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1592,7 +1592,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'e2aeec9de7a742ae131b2f56fc22de6b17b8d0e1',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'b629aee669460dba38accb015d024cc1270ff0f5',
+    Var('webrtc_git') + '/src.git' + '@' + 'e09a174746712aea14c8a87c8cad27c3c871dbc3',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1639,7 +1639,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-amd64',
-          'version': 'b-Qs2BgADASerBkyWLb2sUtPJBkhTpZQJNAwASkgrmcC',
+          'version': 'DPVSayDIq4ga78XaXXcfTzeTE2v33yM8z1w-eaD-ZuQC',
         },
       ],
       'dep_type': 'cipd',
@@ -1653,7 +1653,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b774ad8003fed1f1a56fcf0d891276ce1c290d26',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@3bc201cf43676c573a687909a50ad0a350cab131',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_ssl_host_state_delegate.cc b/android_webview/browser/aw_ssl_host_state_delegate.cc
index 8ede404..5aed7201c 100644
--- a/android_webview/browser/aw_ssl_host_state_delegate.cc
+++ b/android_webview/browser/aw_ssl_host_state_delegate.cc
@@ -61,6 +61,16 @@
   return false;
 }
 
+void AwSSLHostStateDelegate::AllowHttpForHost(const std::string& host) {
+  // Intentional no-op for Android WebView.
+}
+
+bool AwSSLHostStateDelegate::IsHttpAllowedForHost(const std::string& host) {
+  // Intentional no-op for Android WebView. Return value does not matter as
+  // HTTPS-Only Mode is not enabled on WebView.
+  return false;
+}
+
 void AwSSLHostStateDelegate::AllowCert(const std::string& host,
                                        const net::X509Certificate& cert,
                                        int error,
diff --git a/android_webview/browser/aw_ssl_host_state_delegate.h b/android_webview/browser/aw_ssl_host_state_delegate.h
index 7f6af91..b843cd6 100644
--- a/android_webview/browser/aw_ssl_host_state_delegate.h
+++ b/android_webview/browser/aw_ssl_host_state_delegate.h
@@ -73,6 +73,10 @@
                                  int child_id,
                                  InsecureContentType content_type) override;
 
+  // HTTPS-Only Mode is not implemented in Android Webview.
+  void AllowHttpForHost(const std::string& host) override;
+  bool IsHttpAllowedForHost(const std::string& host) override;
+
   // Revokes all SSL certificate error allow exceptions made by the user for
   // |host|.
   void RevokeUserAllowExceptions(const std::string& host) override;
diff --git a/android_webview/browser/renderer_host/aw_render_view_host_ext.cc b/android_webview/browser/renderer_host/aw_render_view_host_ext.cc
index e34da10..933991d 100644
--- a/android_webview/browser/renderer_host/aw_render_view_host_ext.cc
+++ b/android_webview/browser/renderer_host/aw_render_view_host_ext.cc
@@ -74,8 +74,8 @@
     return;
   }
 
-  if (local_main_frame_remote_) {
-    local_main_frame_remote_->DocumentHasImage(std::move(result));
+  if (auto* local_main_frame_remote = GetLocalMainFrameRemote()) {
+    local_main_frame_remote->DocumentHasImage(std::move(result));
   } else {
     // Still have to respond to the API call WebView#docuemntHasImages.
     // Otherwise the listener of the response may be starved.
@@ -98,8 +98,8 @@
   // We only need to get blink::WebView on the renderer side to invoke the
   // blink hit test Mojo method, so sending this message via LocalMainFrame
   // interface is enough.
-  if (local_main_frame_remote_)
-    local_main_frame_remote_->HitTest(touch_center, touch_area);
+  if (auto* local_main_frame_remote = GetLocalMainFrameRemote())
+    local_main_frame_remote->HitTest(touch_center, touch_area);
 }
 
 const mojom::HitTestData& AwRenderViewHostExt::GetLastHitTestData() const {
@@ -109,20 +109,20 @@
 
 void AwRenderViewHostExt::SetTextZoomFactor(float factor) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (local_main_frame_remote_)
-    local_main_frame_remote_->SetTextZoomFactor(factor);
+  if (auto* local_main_frame_remote = GetLocalMainFrameRemote())
+    local_main_frame_remote->SetTextZoomFactor(factor);
 }
 
 void AwRenderViewHostExt::ResetScrollAndScaleState() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (local_main_frame_remote_)
-    local_main_frame_remote_->ResetScrollAndScaleState();
+  if (auto* local_main_frame_remote = GetLocalMainFrameRemote())
+    local_main_frame_remote->ResetScrollAndScaleState();
 }
 
 void AwRenderViewHostExt::SetInitialPageScale(double page_scale_factor) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (local_main_frame_remote_)
-    local_main_frame_remote_->SetInitialPageScale(page_scale_factor);
+  if (auto* local_main_frame_remote = GetLocalMainFrameRemote())
+    local_main_frame_remote->SetInitialPageScale(page_scale_factor);
 }
 
 void AwRenderViewHostExt::SetWillSuppressErrorPage(bool suppress) {
@@ -132,35 +132,8 @@
 void AwRenderViewHostExt::SmoothScroll(int target_x,
                                        int target_y,
                                        base::TimeDelta duration) {
-  if (local_main_frame_remote_)
-    local_main_frame_remote_->SmoothScroll(target_x, target_y, duration);
-}
-
-void AwRenderViewHostExt::ResetLocalMainFrameRemote(
-    content::RenderFrameHost* frame_host) {
-  local_main_frame_remote_.reset();
-  frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
-      local_main_frame_remote_.BindNewEndpointAndPassReceiver());
-}
-
-void AwRenderViewHostExt::RenderFrameCreated(
-    content::RenderFrameHost* frame_host) {
-  // Only handle the active main frame, neither speculative ones nor subframes.
-  if (frame_host != web_contents()->GetMainFrame())
-    return;
-
-  ResetLocalMainFrameRemote(frame_host);
-}
-
-void AwRenderViewHostExt::RenderFrameHostChanged(
-    content::RenderFrameHost* old_host,
-    content::RenderFrameHost* new_host) {
-  // Since we skipped speculative main frames in RenderFrameCreated, we must
-  // watch for them being swapped in by watching for RenderFrameHostChanged().
-  if (new_host != web_contents()->GetMainFrame())
-    return;
-
-  ResetLocalMainFrameRemote(new_host);
+  if (auto* local_main_frame_remote = GetLocalMainFrameRemote())
+    local_main_frame_remote->SmoothScroll(target_x, target_y, duration);
 }
 
 void AwRenderViewHostExt::DidStartNavigation(
@@ -226,4 +199,31 @@
                                 is_main_frame, std::move(callback)));
 }
 
+mojom::LocalMainFrame* AwRenderViewHostExt::GetLocalMainFrameRemote() {
+  // Validate the local main frame matches what we have stored for the current
+  // main frame. Previously `local_main_frame_remote_` was adjusted in
+  // RenderFrameCreated/RenderFrameHostChanged events but the timings of when
+  // this class gets called vs others using this class might cause a TOU
+  // problem, so we validate it each time before use.
+  content::RenderFrameHost* main_frame = web_contents()->GetMainFrame();
+  content::GlobalRenderFrameHostId main_frame_id = main_frame->GetGlobalId();
+  if (main_frame_global_id_ == main_frame_id) {
+    return local_main_frame_remote_.get();
+  }
+
+  local_main_frame_remote_.reset();
+
+  // Avoid accessing GetRemoteAssociatedInterfaces until the renderer is
+  // created.
+  if (!main_frame->IsRenderFrameCreated()) {
+    main_frame_global_id_ = content::GlobalRenderFrameHostId();
+    return nullptr;
+  }
+
+  main_frame_global_id_ = main_frame_id;
+  main_frame->GetRemoteAssociatedInterfaces()->GetInterface(
+      local_main_frame_remote_.BindNewEndpointAndPassReceiver());
+  return local_main_frame_remote_.get();
+}
+
 }  // namespace android_webview
diff --git a/android_webview/browser/renderer_host/aw_render_view_host_ext.h b/android_webview/browser/renderer_host/aw_render_view_host_ext.h
index f2d7011..6cdcf7c 100644
--- a/android_webview/browser/renderer_host/aw_render_view_host_ext.h
+++ b/android_webview/browser/renderer_host/aw_render_view_host_ext.h
@@ -11,6 +11,7 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/sequence_checker.h"
+#include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/web_contents_receiver_set.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
@@ -76,9 +77,6 @@
 
  private:
   // content::WebContentsObserver implementation.
-  void RenderFrameCreated(content::RenderFrameHost* frame_host) override;
-  void RenderFrameHostChanged(content::RenderFrameHost* old_host,
-                              content::RenderFrameHost* new_host) override;
   void DidStartNavigation(
       content::NavigationHandle* navigation_handle) override;
   void DidFinishNavigation(
@@ -96,9 +94,7 @@
       bool is_main_frame,
       ShouldOverrideUrlLoadingCallback callback) override;
 
-  bool IsRenderViewReady() const;
-
-  void ResetLocalMainFrameRemote(content::RenderFrameHost* frame_host);
+  mojom::LocalMainFrame* GetLocalMainFrameRemote();
 
   AwRenderViewHostExtClient* client_;
 
@@ -112,6 +108,8 @@
   // Some WebView users might want to show their own error pages / logic.
   bool will_suppress_error_page_ = false;
 
+  content::GlobalRenderFrameHostId main_frame_global_id_;
+
   content::WebContentsFrameReceiverSet<mojom::FrameHost> frame_host_receivers_;
 
   // Associated channel to the webview LocalMainFrame extensions.
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index f919ea097..5e9ac23 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -116,6 +116,12 @@
                             + "CPU cores on big.LITTLE architectures when the power mode is idle. "
                             + "WebViewCpuAffinityRestrictToLittleCores, if set, takes precedence "
                             + "over this flag."),
+            Flag.baseFeature(PowerSchedulerFeatures.POWER_SCHEDULER,
+                    "Enables the Power Scheduler. Defaults to throttling when idle or in no-op "
+                            + "animations, if at least 250ms of CPU time were spent "
+                            + "in the first 500ms after entering idle/no-op animation mode. "
+                            + "Can be further configured via field trial parameters, "
+                            + "see power_scheduler.h/cc for details."),
             Flag.baseFeature(BlinkFeatures.WEBVIEW_ACCELERATE_SMALL_CANVASES,
                     "Accelerate all canvases in webview."),
             Flag.baseFeature(AwFeatures.WEBVIEW_MIXED_CONTENT_AUTOUPGRADES,
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index b06e378..bed97143 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -962,6 +962,10 @@
 const base::Feature kSystemProxyForSystemServices{
     "SystemProxyForSystemServices", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables the UI to show tab cluster info.
+const base::Feature kTabClusterUI{"TabClusterUI",
+                                  base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables Chrome OS Telemetry Extension.
 const base::Feature kTelemetryExtension{"TelemetryExtension",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
@@ -1466,6 +1470,10 @@
          base::FeatureList::IsEnabled(kSystemLatinPhysicalTyping);
 }
 
+bool IsTabClusterUIEnabled() {
+  return base::FeatureList::IsEnabled(kTabClusterUI);
+}
+
 bool IsTrilinearFilteringEnabled() {
   static bool use_trilinear_filtering =
       base::FeatureList::IsEnabled(kTrilinearFiltering);
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index a2a442f..42487e4 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -361,6 +361,7 @@
 extern const base::Feature kSystemLatinPhysicalTyping;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kSystemProxyForSystemServices;
+COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kTabClusterUI;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kTelemetryExtension;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kTrilinearFiltering;
 COMPONENT_EXPORT(ASH_CONSTANTS)
@@ -505,6 +506,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsSplitSettingsSyncEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsStylusBatteryStatusEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsSystemLatinPhysicalTypingEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsTabClusterUIEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsTrilinearFilteringEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsUseStorkSmdsServerAddressEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsVerticalSplitScreenEnabled();
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index 9376c96..7b45ce49 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -289,6 +289,10 @@
     "system_tray.h",
     "system_tray_client.h",
     "system_tray_observer.h",
+    "tab_cluster/tab_cluster_ui_controller.cc",
+    "tab_cluster/tab_cluster_ui_controller.h",
+    "tab_cluster/tab_cluster_ui_item.cc",
+    "tab_cluster/tab_cluster_ui_item.h",
     "tablet_mode.cc",
     "tablet_mode.h",
     "tablet_mode_observer.h",
diff --git a/ash/public/cpp/tab_cluster/OWNERS b/ash/public/cpp/tab_cluster/OWNERS
new file mode 100644
index 0000000..f5aa337
--- /dev/null
+++ b/ash/public/cpp/tab_cluster/OWNERS
@@ -0,0 +1 @@
+zxdan@chromium.org
diff --git a/ash/public/cpp/tab_cluster/tab_cluster_ui_controller.cc b/ash/public/cpp/tab_cluster/tab_cluster_ui_controller.cc
new file mode 100644
index 0000000..780fff2
--- /dev/null
+++ b/ash/public/cpp/tab_cluster/tab_cluster_ui_controller.cc
@@ -0,0 +1,54 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/public/cpp/tab_cluster/tab_cluster_ui_controller.h"
+
+#include "ash/public/cpp/tab_cluster/tab_cluster_ui_item.h"
+#include "base/containers/unique_ptr_adapters.h"
+
+namespace ash {
+
+TabClusterUIController::TabClusterUIController() = default;
+
+TabClusterUIController::~TabClusterUIController() = default;
+
+TabClusterUIItem* TabClusterUIController::AddTabItem(
+    std::unique_ptr<TabClusterUIItem> tab_item) {
+  auto* tab_item_raw = tab_item.get();
+  tab_items_.push_back(std::move(tab_item));
+  for (auto& observer : observers_)
+    observer.OnTabItemAdded(tab_item_raw);
+  return tab_item_raw;
+}
+
+void TabClusterUIController::UpdateTabItem(TabClusterUIItem* tab_item) {
+  DCHECK(std::find_if(tab_items_.begin(), tab_items_.end(),
+                      base::MatchesUniquePtr(tab_item)) != tab_items_.end());
+  for (auto& observer : observers_)
+    observer.OnTabItemUpdated(tab_item);
+}
+
+void TabClusterUIController::RemoveTabItem(TabClusterUIItem* tab_item) {
+  auto end_iter = tab_items_.end();
+  auto iter = std::find_if(tab_items_.begin(), end_iter,
+                           base::MatchesUniquePtr(tab_item));
+  DCHECK(iter != end_iter);
+  // Since observer may need to use item values, notify observer before removing
+  // from item list.
+  for (auto& observer : observers_)
+    observer.OnTabItemRemoved(tab_item);
+  tab_items_.erase(iter);
+}
+
+void TabClusterUIController::AddObserver(
+    TabClusterUIController::Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void TabClusterUIController::RemoveObserver(
+    TabClusterUIController::Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+}  // namespace ash
diff --git a/ash/public/cpp/tab_cluster/tab_cluster_ui_controller.h b/ash/public/cpp/tab_cluster/tab_cluster_ui_controller.h
new file mode 100644
index 0000000..a7de7d4b
--- /dev/null
+++ b/ash/public/cpp/tab_cluster/tab_cluster_ui_controller.h
@@ -0,0 +1,50 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_PUBLIC_CPP_TAB_CLUSTER_TAB_CLUSTER_UI_CONTROLLER_H_
+#define ASH_PUBLIC_CPP_TAB_CLUSTER_TAB_CLUSTER_UI_CONTROLLER_H_
+
+#include <memory>
+
+#include "ash/public/cpp/ash_public_export.h"
+#include "base/observer_list.h"
+
+namespace ash {
+class TabClusterUIItem;
+
+// TabClusterUIController:
+// Manage the tab items of the opened, modified and closed tabs. When there is
+// a tab item changed, it will notify its observers.
+class ASH_PUBLIC_EXPORT TabClusterUIController {
+ public:
+  class Observer : public base::CheckedObserver {
+   public:
+    virtual void OnTabItemAdded(TabClusterUIItem* tab_item) = 0;
+    virtual void OnTabItemUpdated(TabClusterUIItem* tab_item) = 0;
+    virtual void OnTabItemRemoved(TabClusterUIItem* tab_item) = 0;
+  };
+
+  using TabItems = std::vector<std::unique_ptr<TabClusterUIItem>>;
+
+  TabClusterUIController();
+  TabClusterUIController(const TabClusterUIController&) = delete;
+  TabClusterUIController& operator=(const TabClusterUIController&) = delete;
+
+  ~TabClusterUIController();
+
+  TabClusterUIItem* AddTabItem(std::unique_ptr<TabClusterUIItem> tab_item);
+  void UpdateTabItem(TabClusterUIItem* tab_item);
+  void RemoveTabItem(TabClusterUIItem* tab_item);
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+ private:
+  TabItems tab_items_;
+  base::ObserverList<Observer> observers_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_TAB_CLUSTER_TAB_CLUSTER_UI_CONTROLLER_H_
diff --git a/ash/public/cpp/tab_cluster/tab_cluster_ui_item.cc b/ash/public/cpp/tab_cluster/tab_cluster_ui_item.cc
new file mode 100644
index 0000000..d626009
--- /dev/null
+++ b/ash/public/cpp/tab_cluster/tab_cluster_ui_item.cc
@@ -0,0 +1,35 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/public/cpp/tab_cluster/tab_cluster_ui_item.h"
+
+namespace ash {
+
+////////////////////////////////////////////////////////////////////////////////
+// TabClusterUIItem::Info:
+TabClusterUIItem::Info::Info() = default;
+
+TabClusterUIItem::Info::Info(const Info&) = default;
+
+TabClusterUIItem::Info& TabClusterUIItem::Info::operator=(
+    const TabClusterUIItem::Info&) = default;
+
+TabClusterUIItem::Info::~Info() = default;
+
+////////////////////////////////////////////////////////////////////////////////
+// TabClusterUIItem:
+TabClusterUIItem::TabClusterUIItem() = default;
+
+TabClusterUIItem::TabClusterUIItem(const TabClusterUIItem::Info& info) {
+  Init(info);
+}
+
+TabClusterUIItem::~TabClusterUIItem() = default;
+
+void TabClusterUIItem::Init(const TabClusterUIItem::Info& info) {
+  old_info_ = current_info_;
+  current_info_ = info;
+}
+
+}  // namespace ash
diff --git a/ash/public/cpp/tab_cluster/tab_cluster_ui_item.h b/ash/public/cpp/tab_cluster/tab_cluster_ui_item.h
new file mode 100644
index 0000000..77a9ba8
--- /dev/null
+++ b/ash/public/cpp/tab_cluster/tab_cluster_ui_item.h
@@ -0,0 +1,61 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_PUBLIC_CPP_TAB_CLUSTER_TAB_CLUSTER_UI_ITEM_H_
+#define ASH_PUBLIC_CPP_TAB_CLUSTER_TAB_CLUSTER_UI_ITEM_H_
+
+#include <string>
+
+#include "ash/public/cpp/ash_public_export.h"
+
+namespace aura {
+class Window;
+}  // namespace aura
+
+namespace ash {
+
+// TabClusterUIItem includes realtime info of each tab opened in the browser.
+class ASH_PUBLIC_EXPORT TabClusterUIItem {
+ public:
+  struct Info {
+    Info();
+
+    Info(const Info&);
+    Info& operator=(const Info&);
+
+    ~Info();
+
+    // The tab title.
+    std::string title;
+    // The url or source link of a tab.
+    std::string source;
+    // The cluster to which the tab belongs.
+    int cluster_id = -1;
+    // The browser window that holds the tab's contents.
+    aura::Window* browser_window = nullptr;
+  };
+
+  TabClusterUIItem();
+  explicit TabClusterUIItem(const Info& info);
+  TabClusterUIItem(const TabClusterUIItem&) = delete;
+  TabClusterUIItem& operator=(const TabClusterUIItem&) = delete;
+
+  ~TabClusterUIItem();
+
+  // Load in info.
+  void Init(const Info& info);
+
+  Info current_info() const { return current_info_; }
+  Info old_info() const { return old_info_; }
+
+ private:
+  // Current tab item info.
+  Info current_info_;
+  // The last replaced tab item info.
+  Info old_info_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_TAB_CLUSTER_TAB_CLUSTER_UI_ITEM_H_
diff --git a/ash/shell.cc b/ash/shell.cc
index ce5ed27..0d7e4820 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -85,6 +85,7 @@
 #include "ash/public/cpp/shelf_config.h"
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/public/cpp/tab_cluster/tab_cluster_ui_controller.h"
 #include "ash/public/cpp/views_text_services_context_menu_impl.h"
 #include "ash/quick_answers/quick_answers_controller_impl.h"
 #include "ash/quick_pair/keyed_service/quick_pair_mediator.h"
@@ -842,6 +843,8 @@
   // the active desk is no longer needed.
   desks_controller_.reset();
 
+  tab_cluster_ui_controller_.reset();
+
   focus_rules_ = nullptr;
   focus_controller_.reset();
   screen_position_controller_.reset();
@@ -1039,6 +1042,9 @@
   frame_throttling_controller_ =
       std::make_unique<FrameThrottlingController>(context_factory);
 
+  if (features::IsTabClusterUIEnabled())
+    tab_cluster_ui_controller_ = std::make_unique<TabClusterUIController>();
+
   window_tree_host_manager_->Start();
   AshWindowTreeHostInitParams ash_init_params;
   window_tree_host_manager_->CreatePrimaryHost(ash_init_params);
diff --git a/ash/shell.h b/ash/shell.h
index 4f7aa96..a043fab 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -139,6 +139,7 @@
 class LoginScreenController;
 class LoginUnlockThroughputRecorder;
 class MarkerController;
+class TabClusterUIController;
 class TabletModeController;
 class MediaControllerImpl;
 class MessageCenterAshImpl;
@@ -521,6 +522,9 @@
   SystemTrayNotifier* system_tray_notifier() {
     return system_tray_notifier_.get();
   }
+  TabClusterUIController* tab_cluster_ui_controller() const {
+    return tab_cluster_ui_controller_.get();
+  }
   TabletModeController* tablet_mode_controller() const {
     return tablet_mode_controller_.get();
   }
@@ -729,6 +733,7 @@
   std::unique_ptr<LocaleUpdateControllerImpl> locale_update_controller_;
   std::unique_ptr<LoginScreenController> login_screen_controller_;
   std::unique_ptr<LogoutConfirmationController> logout_confirmation_controller_;
+  std::unique_ptr<TabClusterUIController> tab_cluster_ui_controller_;
   std::unique_ptr<TabletModeController> tablet_mode_controller_;
   std::unique_ptr<MessageCenterAshImpl> message_center_ash_impl_;
   std::unique_ptr<MediaControllerImpl> media_controller_;
diff --git a/ash/system/message_center/unified_message_list_view.cc b/ash/system/message_center/unified_message_list_view.cc
index 2eb63e3..037d946 100644
--- a/ash/system/message_center/unified_message_list_view.cc
+++ b/ash/system/message_center/unified_message_list_view.cc
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 #include "ash/system/message_center/unified_message_list_view.h"
+#include <string>
 
+#include "ash/public/cpp/metrics_util.h"
 #include "ash/system/message_center/message_center_style.h"
 #include "ash/system/message_center/message_center_utils.h"
 #include "ash/system/message_center/metrics_utils.h"
@@ -12,7 +14,10 @@
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/unified_system_tray_model.h"
 #include "base/auto_reset.h"
+#include "base/callback_forward.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
+#include "ui/compositor/compositor.h"
 #include "ui/gfx/animation/linear_animation.h"
 #include "ui/gfx/canvas.h"
 #include "ui/message_center/message_center.h"
@@ -21,6 +26,7 @@
 #include "ui/views/border.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
+#include "ui/views/widget/widget.h"
 
 using message_center::MessageCenter;
 using message_center::MessageView;
@@ -37,6 +43,31 @@
 constexpr base::TimeDelta kClearAllVisibleAnimationDuration =
     base::TimeDelta::FromMilliseconds(160);
 
+constexpr char kMoveDownAnimationSmoothnessHistogramName[] =
+    "Ash.Notification.MoveDown.AnimationSmoothness";
+constexpr char kClearAllStackedAnimationSmoothnessHistogramName[] =
+    "Ash.Notification.ClearAllStacked.AnimationSmoothness";
+constexpr char kClearAllVisibleAnimationSmoothnessHistogramName[] =
+    "Ash.Notification.ClearAllVisible.AnimationSmoothness";
+
+void RecordAnimationSmoothness(const std::string& histogram_name,
+                               int smoothness) {
+  base::UmaHistogramPercentage(histogram_name, smoothness);
+}
+
+void SetupThroughputTrackerForAnimationSmoothness(
+    views::Widget* widget,
+    absl::optional<ui::ThroughputTracker>& tracker,
+    const char* histogram_name) {
+  // `widget` may not exist in tests.
+  if (!widget)
+    return;
+
+  tracker.emplace(widget->GetCompositor()->RequestNewThroughputTracker());
+  tracker->Start(ash::metrics_util::ForSmoothness(
+      base::BindRepeating(&RecordAnimationSmoothness, histogram_name)));
+}
+
 }  // namespace
 
 // Container view of notification and swipe control.
@@ -475,6 +506,12 @@
 }
 
 void UnifiedMessageListView::AnimationEnded(const gfx::Animation* animation) {
+  if (throughput_tracker_) {
+    // Reset `throughput_tracker_` to reset animation metrics recording.
+    throughput_tracker_->Stop();
+    throughput_tracker_.reset();
+  }
+
   // This is also called from AnimationCanceled().
   animation_->SetCurrentValue(1.0);
   PreferredSizeChanged();
@@ -623,22 +660,33 @@
 void UnifiedMessageListView::StartAnimation() {
   DCHECK_NE(state_, State::IDLE);
 
+  base::TimeDelta animation_duration;
+
   switch (state_) {
     case State::IDLE:
       break;
     case State::MOVE_DOWN:
-      animation_->SetDuration(kClosingAnimationDuration);
-      animation_->Start();
+      SetupThroughputTrackerForAnimationSmoothness(
+          GetWidget(), throughput_tracker_,
+          kMoveDownAnimationSmoothnessHistogramName);
+      animation_duration = kClosingAnimationDuration;
       break;
     case State::CLEAR_ALL_STACKED:
-      animation_->SetDuration(kClearAllStackedAnimationDuration);
-      animation_->Start();
+      SetupThroughputTrackerForAnimationSmoothness(
+          GetWidget(), throughput_tracker_,
+          kClearAllStackedAnimationSmoothnessHistogramName);
+      animation_duration = kClearAllStackedAnimationDuration;
       break;
     case State::CLEAR_ALL_VISIBLE:
-      animation_->SetDuration(kClearAllVisibleAnimationDuration);
-      animation_->Start();
+      SetupThroughputTrackerForAnimationSmoothness(
+          GetWidget(), throughput_tracker_,
+          kClearAllVisibleAnimationSmoothnessHistogramName);
+      animation_duration = kClearAllVisibleAnimationDuration;
       break;
   }
+
+  animation_->SetDuration(animation_duration);
+  animation_->Start();
 }
 
 void UnifiedMessageListView::UpdateClearAllAnimation() {
@@ -660,8 +708,6 @@
       }
 
       PreferredSizeChanged();
-
-      state_ = State::CLEAR_ALL_STACKED;
     } else {
       state_ = State::CLEAR_ALL_VISIBLE;
     }
diff --git a/ash/system/message_center/unified_message_list_view.h b/ash/system/message_center/unified_message_list_view.h
index 538ce32..bfbb3d83 100644
--- a/ash/system/message_center/unified_message_list_view.h
+++ b/ash/system/message_center/unified_message_list_view.h
@@ -6,6 +6,7 @@
 #define ASH_SYSTEM_MESSAGE_CENTER_UNIFIED_MESSAGE_LIST_VIEW_H_
 
 #include "ash/ash_export.h"
+#include "ui/compositor/throughput_tracker.h"
 #include "ui/message_center/message_center_observer.h"
 #include "ui/message_center/views/message_view.h"
 #include "ui/views/animation/animation_delegate_views.h"
@@ -69,7 +70,7 @@
   // Returns the total number of pinned notifications in the list.
   int GetTotalPinnedNotificationCount() const;
 
-  // Returns true if an animation is currently in progress.
+  // Returns true if `animation_` is currently in progress.
   bool IsAnimating() const;
 
   // Called when a notification is slid out so we can run the MOVE_DOWN
@@ -118,8 +119,8 @@
   class Background;
   class MessageViewContainer;
 
-  // UnifiedMessageListView always runs single animation at one time. When
-  // |state_| is IDLE, animation_->is_animating() is always false and vice
+  // UnifiedMessageListView always runs a single animation at one time. When
+  // `state_` is IDLE, `animation_->is_animating()` is always false and vice
   // versa.
   enum class State {
     // No animation is running.
@@ -200,6 +201,9 @@
   // implicit animation.
   const std::unique_ptr<gfx::LinearAnimation> animation_;
 
+  // Measure animation smoothness metrics for `animation_`.
+  absl::optional<ui::ThroughputTracker> throughput_tracker_;
+
   State state_ = State::IDLE;
 
   // The height the UnifiedMessageListView starts animating from. If not
diff --git a/ash/system/network/cellular_setup_notifier.cc b/ash/system/network/cellular_setup_notifier.cc
index 73c21bc..fb77e01c 100644
--- a/ash/system/network/cellular_setup_notifier.cc
+++ b/ash/system/network/cellular_setup_notifier.cc
@@ -90,6 +90,8 @@
     : timer_(std::make_unique<base::OneShotTimer>()) {
   GetNetworkConfigService(
       remote_cros_network_config_.BindNewPipeAndPassReceiver());
+  remote_cros_network_config_->AddObserver(
+      cros_network_config_observer_receiver_.BindNewPipeAndPassRemote());
   Shell::Get()->session_controller()->AddObserver(this);
 }
 
@@ -121,6 +123,30 @@
 }
 
 void CellularSetupNotifier::OnTimerFired() {
+  timer_fired_ = true;
+  MaybeShowCellularSetupNotification();
+}
+
+void CellularSetupNotifier::OnNetworkStateListChanged() {
+  MaybeShowCellularSetupNotification();
+}
+
+void CellularSetupNotifier::OnNetworkStateChanged(
+    chromeos::network_config::mojom::NetworkStatePropertiesPtr network) {
+  if (network->type !=
+          chromeos::network_config::mojom::NetworkType::kCellular ||
+      network->type_state->get_cellular()->activation_state !=
+          chromeos::network_config::mojom::ActivationStateType::kActivated) {
+    return;
+  }
+
+  SetCellularSetupNotificationCannotBeShown();
+  message_center::MessageCenter* message_center =
+      message_center::MessageCenter::Get();
+  message_center->RemoveNotification(kCellularSetupNotificationId, false);
+}
+
+void CellularSetupNotifier::MaybeShowCellularSetupNotification() {
   remote_cros_network_config_->GetDeviceStateList(base::BindOnce(
       &CellularSetupNotifier::OnGetDeviceStateList, base::Unretained(this)));
 }
@@ -155,9 +181,19 @@
       // keep starting the timer for a user who already has an activated
       // cellular network.
       SetCellularSetupNotificationCannotBeShown();
+      message_center::MessageCenter* message_center =
+          message_center::MessageCenter::Get();
+      message_center->RemoveNotification(kCellularSetupNotificationId, false);
       return;
     }
   }
+
+  // Do not show notification if it has already been shown, or the timer
+  // has not yet been fired.
+  if (!GetCanCellularSetupNotificationBeShown() || !timer_fired_) {
+    return;
+  }
+
   ShowCellularSetupNotification();
 }
 
diff --git a/ash/system/network/cellular_setup_notifier.h b/ash/system/network/cellular_setup_notifier.h
index 041d49e..45f5da3 100644
--- a/ash/system/network/cellular_setup_notifier.h
+++ b/ash/system/network/cellular_setup_notifier.h
@@ -22,7 +22,9 @@
 // Notifies the user after OOBE to finish setting up their cellular network if
 // user has a device with eSIM but no profiles have been configured, or they
 // inserted a cold pSIM and need to provision in-session.
-class ASH_EXPORT CellularSetupNotifier : public SessionObserver {
+class ASH_EXPORT CellularSetupNotifier
+    : public SessionObserver,
+      public chromeos::network_config::mojom::CrosNetworkConfigObserver {
  public:
   CellularSetupNotifier();
   CellularSetupNotifier(const CellularSetupNotifier&) = delete;
@@ -51,6 +53,19 @@
   // SessionObserver:
   void OnSessionStateChanged(session_manager::SessionState state) override;
 
+  // CrosNetworkConfigObserver:
+  void OnNetworkStateListChanged() override;
+  void OnActiveNetworksChanged(
+      std::vector<chromeos::network_config::mojom::NetworkStatePropertiesPtr>
+          networks) override {}
+  void OnNetworkStateChanged(
+      chromeos::network_config::mojom::NetworkStatePropertiesPtr network)
+      override;
+  void OnDeviceStateListChanged() override {}
+  void OnVpnProvidersChanged() override {}
+  void OnNetworkCertificatesChanged() override {}
+
+  void MaybeShowCellularSetupNotification();
   void OnTimerFired();
   void OnGetDeviceStateList(
       std::vector<chromeos::network_config::mojom::DeviceStatePropertiesPtr>
@@ -67,8 +82,11 @@
 
   mojo::Remote<chromeos::network_config::mojom::CrosNetworkConfig>
       remote_cros_network_config_;
+  mojo::Receiver<chromeos::network_config::mojom::CrosNetworkConfigObserver>
+      cros_network_config_observer_receiver_{this};
 
   std::unique_ptr<base::OneShotTimer> timer_;
+  bool timer_fired_{false};
 };
 
 }  // namespace ash
diff --git a/ash/system/network/cellular_setup_notifier_unittest.cc b/ash/system/network/cellular_setup_notifier_unittest.cc
index c3186ae5e..ce21ceb9 100644
--- a/ash/system/network/cellular_setup_notifier_unittest.cc
+++ b/ash/system/network/cellular_setup_notifier_unittest.cc
@@ -20,6 +20,7 @@
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/system_token_cert_db_storage.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_test_helper.h"
+#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
 #include "components/prefs/pref_service.h"
 #include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
 #include "ui/message_center/message_center.h"
@@ -224,4 +225,37 @@
   ASSERT_FALSE(mock_notification_timer_->IsRunning());
 }
 
+TEST_F(CellularSetupNotifierTest, RemoveNotificationAfterAddingNetwork) {
+  network_config_helper_->network_state_helper().AddDevice(
+      kShillManagerClientStubCellularDevice, shill::kTypeCellular,
+      kShillManagerClientStubCellularDeviceName);
+
+  LogInAndFireTimer();
+
+  message_center::Notification* notification = GetCellularSetupNotification();
+  EXPECT_TRUE(notification);
+  EXPECT_FALSE(GetCanCellularSetupNotificationBeShown());
+
+  const std::string& cellular_path_ =
+      network_config_helper_->network_state_helper().ConfigureService(
+          R"({"GUID": "cellular_guid", "Type": "cellular", "Technology": "LTE",
+            "State": "idle"})");
+
+  base::RunLoop().RunUntilIdle();
+
+  // Notification is not removed after adding unactivated network.
+  notification = GetCellularSetupNotification();
+  EXPECT_TRUE(notification);
+
+  network_config_helper_->network_state_helper().SetServiceProperty(
+      cellular_path_, shill::kActivationStateProperty,
+      base::Value(shill::kActivationStateActivated));
+
+  base::RunLoop().RunUntilIdle();
+
+  notification = GetCellularSetupNotification();
+  EXPECT_FALSE(notification);
+  ASSERT_FALSE(mock_notification_timer_->IsRunning());
+}
+
 }  // namespace ash
diff --git a/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js b/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js
index 7ef0a41..b61ad18 100644
--- a/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js
+++ b/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js
@@ -173,8 +173,7 @@
    * @return {!Promise<!StateResult>}
    */
   updateChrome() {
-    return this.getNextStateForMethod_(
-      'updateChrome', RmaState.kUpdateChrome);
+    return this.getNextStateForMethod_('updateChrome', RmaState.kUpdateChrome);
   }
 
   /**
@@ -182,7 +181,7 @@
    */
   updateChromeSkipped() {
     return this.getNextStateForMethod_(
-      'updateChromeSkipped', RmaState.kUpdateChrome);
+        'updateChromeSkipped', RmaState.kUpdateChrome);
   }
 
   /**
@@ -264,7 +263,7 @@
    */
   setComponentList(components) {
     return this.getNextStateForMethod_(
-      'setComponentList', RmaState.kSelectComponents);
+        'setComponentList', RmaState.kSelectComponents);
   }
 
   /**
@@ -393,7 +392,7 @@
   setDeviceInformation(serialNumber, regionIndex, skuIndex) {
     // TODO(gavindodd): Validate range of region and sku.
     return this.getNextStateForMethod_(
-      'setDeviceInformation', RmaState.kUpdateDeviceInformation);
+        'setDeviceInformation', RmaState.kUpdateDeviceInformation);
   }
 
   /**
@@ -401,7 +400,7 @@
    */
   finalizeAndReboot() {
     return this.getNextStateForMethod_(
-      'finalizeAndReboot', RmaState.kRepairComplete);
+        'finalizeAndReboot', RmaState.kRepairComplete);
   }
 
   /**
@@ -409,7 +408,7 @@
    */
   finalizeAndShutdown() {
     return this.getNextStateForMethod_(
-      'finalizeAndShutdown', RmaState.kRepairComplete);
+        'finalizeAndShutdown', RmaState.kRepairComplete);
   }
 
   /**
@@ -417,7 +416,7 @@
    */
   cutoffBattery() {
     return this.getNextStateForMethod_(
-      'cutoffBattery', RmaState.kRepairComplete);
+        'cutoffBattery', RmaState.kRepairComplete);
   }
 
   /**
@@ -570,11 +569,12 @@
    * @template T
    */
   triggerObserverAfterMs(method, result, delayMs) {
-    let setDataTriggerAndResolve = function (service, resolve) {
+    let setDataTriggerAndResolve = function(service, resolve) {
       service.observables_.setObservableData(method, [result]);
       service.observables_.trigger(method);
       resolve();
-    }
+    };
+
     return new Promise((resolve) => {
       if (delayMs === 0) {
         setDataTriggerAndResolve(this, resolve);
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 0f8ab69..576bd49 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -842,8 +842,10 @@
     "trace_event/trace_id_helper.h",
     "traits_bag.h",
     "tuple.h",
+    "types/id_type.h",
     "types/pass_key.h",
     "types/strong_alias.h",
+    "types/token_type.h",
     "unguessable_token.cc",
     "unguessable_token.h",
     "updateable_sequenced_task_runner.h",
@@ -3151,8 +3153,10 @@
     "tools_sanity_unittest.cc",
     "traits_bag_unittest.cc",
     "tuple_unittest.cc",
+    "types/id_type_unittest.cc",
     "types/pass_key_unittest.cc",
     "types/strong_alias_unittest.cc",
+    "types/token_type_unittest.cc",
     "unguessable_token_unittest.cc",
     "value_iterators_unittest.cc",
     "values_unittest.cc",
diff --git a/base/debug/activity_tracker.cc b/base/debug/activity_tracker.cc
index 77e45f5..8595e56 100644
--- a/base/debug/activity_tracker.cc
+++ b/base/debug/activity_tracker.cc
@@ -29,6 +29,10 @@
 #include "base/threading/platform_thread.h"
 #include "build/build_config.h"
 
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
 namespace base {
 namespace debug {
 
diff --git a/base/files/file_unittest.cc b/base/files/file_unittest.cc
index 6bcc617..c1dbb8b 100644
--- a/base/files/file_unittest.cc
+++ b/base/files/file_unittest.cc
@@ -21,6 +21,10 @@
 #include "third_party/perfetto/include/perfetto/test/traced_value_test_support.h"  // no-presubmit-check nogncheck
 #endif  // BUILDFLAG(ENABLE_BASE_TRACING)
 
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
 using base::File;
 using base::FilePath;
 
diff --git a/base/files/file_util.cc b/base/files/file_util.cc
index d2065ce..5b801a4 100644
--- a/base/files/file_util.cc
+++ b/base/files/file_util.cc
@@ -24,6 +24,10 @@
 #include "base/threading/scoped_blocking_call.h"
 #include "build/build_config.h"
 
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
 namespace base {
 
 #if !defined(OS_NACL_NONSFI)
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index 14b0880..c466833 100644
--- a/base/metrics/field_trial.cc
+++ b/base/metrics/field_trial.cc
@@ -31,6 +31,10 @@
 #include "base/posix/global_descriptors.h"
 #endif
 
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
 namespace base {
 
 namespace {
diff --git a/base/observer_list.h b/base/observer_list.h
index 59414bf..a62fa36 100644
--- a/base/observer_list.h
+++ b/base/observer_list.h
@@ -336,14 +336,17 @@
   }
 
   std::string GetObserversCreationStackString() const {
+#if EXPENSIVE_DCHECKS_ARE_ON()
     std::string result;
-#if DCHECK_IS_ON()
     for (const auto& observer : observers_) {
       result += observer.GetCreationStackString();
       result += "\n";
     }
-#endif
     return result;
+#else
+    return "For observer stack traces, build with "
+           "`enable_expensive_dchecks=true`.";
+#endif  // EXPENSIVE_DCHECKS_ARE_ON()
   }
 
   std::vector<ObserverStorageType> observers_;
diff --git a/base/observer_list_internal.h b/base/observer_list_internal.h
index 08abd8e..d1e1f15 100644
--- a/base/observer_list_internal.h
+++ b/base/observer_list_internal.h
@@ -42,15 +42,15 @@
     return static_cast<ObserverType*>(adapter.ptr_);
   }
 
-#if DCHECK_IS_ON()
+#if EXPENSIVE_DCHECKS_ARE_ON()
   std::string GetCreationStackString() const { return stack_.ToString(); }
-#endif
+#endif  // EXPENSIVE_DCHECKS_ARE_ON()
 
  private:
   void* ptr_;
-#if DCHECK_IS_ON()
+#if EXPENSIVE_DCHECKS_ARE_ON()
   base::debug::StackTrace stack_;
-#endif
+#endif  // EXPENSIVE_DCHECKS_ARE_ON()
 };
 
 // Adapter for CheckedObserver types so that they can use the same syntax as a
diff --git a/base/process/launch_win.cc b/base/process/launch_win.cc
index 017b7e6..4f1e44d7 100644
--- a/base/process/launch_win.cc
+++ b/base/process/launch_win.cc
@@ -6,10 +6,13 @@
 
 #include <fcntl.h>
 #include <io.h>
-#include <shellapi.h>
+
+// windows.h must be included before shellapi.h
 #include <windows.h>
-#include <userenv.h>
+
 #include <psapi.h>
+#include <shellapi.h>
+#include <userenv.h>
 
 #include <ios>
 #include <limits>
diff --git a/base/process/process_unittest.cc b/base/process/process_unittest.cc
index 2c7c515..b74ba48 100644
--- a/base/process/process_unittest.cc
+++ b/base/process/process_unittest.cc
@@ -21,6 +21,8 @@
 #if defined(OS_WIN)
 #include "base/win/base_win_buildflags.h"
 #include "base/win/windows_version.h"
+
+#include <windows.h>
 #endif
 
 namespace {
diff --git a/base/task/single_thread_task_executor_unittest.cc b/base/task/single_thread_task_executor_unittest.cc
index 7dd0bd3..9d33a94 100644
--- a/base/task/single_thread_task_executor_unittest.cc
+++ b/base/task/single_thread_task_executor_unittest.cc
@@ -55,6 +55,8 @@
 #include "base/win/current_module.h"
 #include "base/win/message_window.h"
 #include "base/win/scoped_handle.h"
+
+#include <windows.h>
 #endif
 
 using ::testing::IsNull;
@@ -363,11 +365,11 @@
   // Poll for the MessageBox. Don't do this at home! At the speed we do it,
   // you will never realize one MessageBox was shown.
   for (; expect_window;) {
-    HWND window = ::FindWindow(L"#32770", kMessageBoxTitle);
+    HWND window = ::FindWindowW(L"#32770", kMessageBoxTitle);
     if (window) {
       // Dismiss it.
       for (;;) {
-        HWND button = ::FindWindowEx(window, NULL, L"Button", NULL);
+        HWND button = ::FindWindowExW(window, NULL, L"Button", NULL);
         if (button != NULL) {
           EXPECT_EQ(0, ::SendMessage(button, WM_LBUTTONDOWN, 0, 0));
           EXPECT_EQ(0, ::SendMessage(button, WM_LBUTTONUP, 0, 0));
diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc
index fc615fc..84fa883 100644
--- a/base/test/launcher/test_launcher.cc
+++ b/base/test/launcher/test_launcher.cc
@@ -73,6 +73,8 @@
 #if defined(OS_WIN)
 #include "base/strings/string_util_win.h"
 #include "base/win/windows_version.h"
+
+#include <windows.h>
 #endif
 
 #if defined(OS_FUCHSIA)
diff --git a/base/test/test_suite.cc b/base/test/test_suite.cc
index c9114954..d886d04 100644
--- a/base/test/test_suite.cc
+++ b/base/test/test_suite.cc
@@ -76,9 +76,12 @@
 #include "base/base_paths_fuchsia.h"
 #endif
 
-#if defined(OS_WIN) && defined(_DEBUG)
+#if defined(OS_WIN)
+#if defined(_DEBUG)
 #include <crtdbg.h>
-#endif
+#endif  // _DEBUG
+#include <windows.h>
+#endif  // OS_WIN
 
 namespace base {
 
diff --git a/base/util/type_safety/id_type.h b/base/types/id_type.h
similarity index 91%
rename from base/util/type_safety/id_type.h
rename to base/types/id_type.h
index 98f2414..298dc98 100644
--- a/base/util/type_safety/id_type.h
+++ b/base/types/id_type.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef BASE_UTIL_TYPE_SAFETY_ID_TYPE_H_
-#define BASE_UTIL_TYPE_SAFETY_ID_TYPE_H_
+#ifndef BASE_TYPES_ID_TYPE_H_
+#define BASE_TYPES_ID_TYPE_H_
 
 #include <cstdint>
 #include <type_traits>
 
 #include "base/types/strong_alias.h"
 
-namespace util {
+namespace base {
 
 // A specialization of StrongAlias for integer-based identifiers.
 //
@@ -51,7 +51,7 @@
           typename WrappedType,
           WrappedType kInvalidValue,
           WrappedType kFirstGeneratedId = kInvalidValue + 1>
-class IdType : public base::StrongAlias<TypeMarker, WrappedType> {
+class IdType : public StrongAlias<TypeMarker, WrappedType> {
  public:
   static_assert(
       std::is_unsigned<WrappedType>::value || kInvalidValue <= 0,
@@ -67,7 +67,7 @@
                 "invalid value so that the monotonically increasing "
                 "GenerateNextId method will never return the invalid value.");
 
-  using base::StrongAlias<TypeMarker, WrappedType>::StrongAlias;
+  using StrongAlias<TypeMarker, WrappedType>::StrongAlias;
 
   // This class can be used to generate unique IdTypes. It keeps an internal
   // counter that is continually increased by one every time an ID is generated.
@@ -88,8 +88,7 @@
 
   // Default-construct in the null state.
   constexpr IdType()
-      : base::StrongAlias<TypeMarker, WrappedType>::StrongAlias(kInvalidValue) {
-  }
+      : StrongAlias<TypeMarker, WrappedType>::StrongAlias(kInvalidValue) {}
 
   constexpr bool is_null() const { return this->value() == kInvalidValue; }
   constexpr explicit operator bool() const { return !is_null(); }
@@ -112,6 +111,7 @@
 using IdType64 = IdType<TypeMarker, std::int64_t, 0>;
 template <typename TypeMarker>
 using IdTypeU64 = IdType<TypeMarker, std::uint64_t, 0>;
-}  // namespace util
 
-#endif  // BASE_UTIL_TYPE_SAFETY_ID_TYPE_H_
+}  // namespace base
+
+#endif  // BASE_TYPES_ID_TYPE_H_
diff --git a/base/util/type_safety/id_type_unittest.cc b/base/types/id_type_unittest.cc
similarity index 97%
rename from base/util/type_safety/id_type_unittest.cc
rename to base/types/id_type_unittest.cc
index a2dc000..e923f2a 100644
--- a/base/util/type_safety/id_type_unittest.cc
+++ b/base/types/id_type_unittest.cc
@@ -4,10 +4,10 @@
 
 #include <limits>
 
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace util {
+namespace base {
 
 namespace {
 
@@ -110,4 +110,4 @@
                                            123,
                                            std::numeric_limits<int>::max()));
 
-}  // namespace util
+}  // namespace base
diff --git a/base/types/strong_alias.h b/base/types/strong_alias.h
index 394b01d..a80bbe4a 100644
--- a/base/types/strong_alias.h
+++ b/base/types/strong_alias.h
@@ -68,10 +68,10 @@
 // See also
 // - //styleguide/c++/blink-c++.md which provides recommendation and examples of
 //   using StrongAlias<Tag, bool> instead of a bare bool.
-// - util::IdType<...> which provides helpers for specializing
-//   StrongAlias to be used as an id.
-// - util::TokenType<...> which provides helpers for specializing StrongAlias
-//   to be used as a wrapper of base::UnguessableToken.
+// - IdType<...> which provides helpers for specializing StrongAlias to be
+//   used as an id.
+// - TokenType<...> which provides helpers for specializing StrongAlias to be
+//   used as a wrapper of base::UnguessableToken.
 template <typename TagType, typename UnderlyingType>
 class StrongAlias {
  public:
diff --git a/base/types/token_type.h b/base/types/token_type.h
new file mode 100644
index 0000000..7c09ce04
--- /dev/null
+++ b/base/types/token_type.h
@@ -0,0 +1,53 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TYPES_TOKEN_TYPE_H_
+#define BASE_TYPES_TOKEN_TYPE_H_
+
+#include <type_traits>
+
+#include "base/types/strong_alias.h"
+#include "base/unguessable_token.h"
+
+namespace base {
+
+// A specialization of StrongAlias for UnguessableToken. Unlike
+// UnguessableToken, a TokenType<...> does not default to null and does not
+// expose the concept of null tokens. If you need to indicate a null token,
+// please use absl::optional<TokenType<...>>.
+template <typename TypeMarker>
+class TokenType : public StrongAlias<TypeMarker, UnguessableToken> {
+ private:
+  using Super = StrongAlias<TypeMarker, UnguessableToken>;
+
+ public:
+  TokenType() : Super(UnguessableToken::Create()) {}
+  // The parameter |unused| is here to prevent multiple definitions of a
+  // single argument constructor. This is only needed during the migration to
+  // strongly typed frame tokens.
+  explicit TokenType(const UnguessableToken& token) : Super(token) {}
+  TokenType(const TokenType& token) : Super(token.value()) {}
+  TokenType(TokenType&& token) noexcept : Super(token.value()) {}
+  TokenType& operator=(const TokenType& token) = default;
+  TokenType& operator=(TokenType&& token) noexcept = default;
+
+  // This object allows default assignment operators for compatibility with
+  // STL containers.
+
+  // Hash functor for use in unordered containers.
+  struct Hasher {
+    using argument_type = TokenType;
+    using result_type = size_t;
+    result_type operator()(const argument_type& token) const {
+      return UnguessableTokenHash()(token.value());
+    }
+  };
+
+  // Mimic the UnguessableToken API for ease and familiarity of use.
+  std::string ToString() const { return this->value().ToString(); }
+};
+
+}  // namespace base
+
+#endif  // BASE_TYPES_TOKEN_TYPE_H_
diff --git a/base/util/type_safety/token_type_unittest.cc b/base/types/token_type_unittest.cc
similarity index 85%
rename from base/util/type_safety/token_type_unittest.cc
rename to base/types/token_type_unittest.cc
index 41a54d8..aec69a6 100644
--- a/base/util/type_safety/token_type_unittest.cc
+++ b/base/types/token_type_unittest.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/util/type_safety/token_type.h"
+#include "base/types/token_type.h"
 
 #include "base/unguessable_token.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace util {
+namespace base {
 
 using FooToken = TokenType<class Foo>;
 
@@ -37,11 +37,10 @@
   EXPECT_TRUE(token1 != token4);
 
   // Test hasher.
-  EXPECT_EQ(FooToken::Hasher()(token2),
-            base::UnguessableTokenHash()(token2.value()));
+  EXPECT_EQ(FooToken::Hasher()(token2), UnguessableTokenHash()(token2.value()));
 
   // Test string representation.
   EXPECT_EQ(token2.ToString(), token2.value().ToString());
 }
 
-}  // namespace util
+}  // namespace base
diff --git a/base/util/BUILD.gn b/base/util/BUILD.gn
index af2cd88..db00b5b 100644
--- a/base/util/BUILD.gn
+++ b/base/util/BUILD.gn
@@ -9,7 +9,6 @@
     "enum_set:unittests",
     "memory_pressure:unittests",
     "timer:unittests",
-    "type_safety:tests",
     "values:unittests",
     "//base/test:run_all_unittests",
   ]
diff --git a/base/util/type_safety/BUILD.gn b/base/util/type_safety/BUILD.gn
deleted file mode 100644
index 760e3b0..0000000
--- a/base/util/type_safety/BUILD.gn
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (c) 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/nocompile.gni")
-
-# Change this target's type to component if it starts to contain more than
-# just headers. Header-only targets cannot be compiled to libraries, so it must
-# remain a source_set for now.
-source_set("type_safety") {
-  sources = [
-    "id_type.h",
-    "token_type.h",
-  ]
-
-  deps = [ "//base" ]
-}
-
-source_set("tests") {
-  testonly = true
-  sources = [
-    "id_type_unittest.cc",
-    "token_type_unittest.cc",
-  ]
-
-  deps = [
-    ":type_safety",
-    "//base",
-    "//testing/gtest",
-  ]
-}
diff --git a/base/util/type_safety/DEPS b/base/util/type_safety/DEPS
deleted file mode 100644
index c179df4..0000000
--- a/base/util/type_safety/DEPS
+++ /dev/null
@@ -1,7 +0,0 @@
-include_rules = [
-  "-base",
-  "+base/types",
-  "+base/unguessable_token.h",
-  "+base/util/type_safety",
-  "-third_party",
-]
diff --git a/base/util/type_safety/OWNERS b/base/util/type_safety/OWNERS
deleted file mode 100644
index 192ba64..0000000
--- a/base/util/type_safety/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-lukasza@chromium.org
-mpawlowski@opera.com
diff --git a/base/util/type_safety/token_type.h b/base/util/type_safety/token_type.h
deleted file mode 100644
index 84e48e7c..0000000
--- a/base/util/type_safety/token_type.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_UTIL_TYPE_SAFETY_TOKEN_TYPE_H_
-#define BASE_UTIL_TYPE_SAFETY_TOKEN_TYPE_H_
-
-#include <type_traits>
-
-#include "base/types/strong_alias.h"
-#include "base/unguessable_token.h"
-
-namespace util {
-
-// A specialization of StrongAlias for base::UnguessableToken. Unlike
-// base::UnguessableToken, a TokenType<...> does not default to null and does
-// not expose the concept of null tokens. If you need to indicate a null token,
-// please use absl::optional<TokenType<...>>.
-template <typename TypeMarker>
-class TokenType : public base::StrongAlias<TypeMarker, base::UnguessableToken> {
- private:
-  using Super = base::StrongAlias<TypeMarker, base::UnguessableToken>;
-
- public:
-  TokenType() : Super(base::UnguessableToken::Create()) {}
-  // The parameter |unused| is here to prevent multiple definitions of a
-  // single argument constructor. This is only needed during the migration to
-  // strongly typed frame tokens.
-  explicit TokenType(const base::UnguessableToken& token) : Super(token) {}
-  TokenType(const TokenType& token) : Super(token.value()) {}
-  TokenType(TokenType&& token) noexcept : Super(token.value()) {}
-  TokenType& operator=(const TokenType& token) = default;
-  TokenType& operator=(TokenType&& token) noexcept = default;
-
-  // This object allows default assignment operators for compatibility with
-  // STL containers.
-
-  // Hash functor for use in unordered containers.
-  struct Hasher {
-    using argument_type = TokenType;
-    using result_type = size_t;
-    result_type operator()(const argument_type& token) const {
-      return base::UnguessableTokenHash()(token.value());
-    }
-  };
-
-  // Mimic the base::UnguessableToken API for ease and familiarity of use.
-  std::string ToString() const { return this->value().ToString(); }
-};
-
-}  // namespace util
-
-#endif  // BASE_UTIL_TYPE_SAFETY_TOKEN_TYPE_H_
diff --git a/base/win/message_window.cc b/base/win/message_window.cc
index fee37b0..4525f79e 100644
--- a/base/win/message_window.cc
+++ b/base/win/message_window.cc
@@ -11,6 +11,8 @@
 #include "base/win/current_module.h"
 #include "base/win/wrapped_window_proc.h"
 
+#include <windows.h>
+
 const wchar_t kMessageWindowClassName[] = L"Chrome_MessageWindow";
 
 namespace base {
diff --git a/base/win/message_window_unittest.cc b/base/win/message_window_unittest.cc
index 99e26ce..0193b42 100644
--- a/base/win/message_window_unittest.cc
+++ b/base/win/message_window_unittest.cc
@@ -9,6 +9,8 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#include <windows.h>
+
 namespace base {
 
 namespace {
diff --git a/base/win/win_includes_unittest.cc b/base/win/win_includes_unittest.cc
index 322eaed..31177fa 100644
--- a/base/win/win_includes_unittest.cc
+++ b/base/win/win_includes_unittest.cc
@@ -27,10 +27,17 @@
 // Make sure windows.h can be included after windows_types.h
 #include "base/win/windows_types.h"
 
+// windows.h must be included before objidl.h
 #include <windows.h>
 
+#include <objidl.h>
+
 // Check that type sizes match.
 static_assert(sizeof(CHROME_CONDITION_VARIABLE) == sizeof(CONDITION_VARIABLE),
               "Definition mismatch.");
 static_assert(sizeof(CHROME_SRWLOCK) == sizeof(SRWLOCK),
               "Definition mismatch.");
+static_assert(sizeof(CHROME_WIN32_FIND_DATA) == sizeof(WIN32_FIND_DATA),
+              "Definition mismatch.");
+static_assert(sizeof(CHROME_FORMATETC) == sizeof(FORMATETC),
+              "Definition mismatch.");
diff --git a/base/win/windows_types.h b/base/win/windows_types.h
index 971dfdd..5fe4ffc 100644
--- a/base/win/windows_types.h
+++ b/base/win/windows_types.h
@@ -125,10 +125,22 @@
 
 typedef HANDLE HLOCAL;
 
+typedef /* [wire_marshal] */ WORD CLIPFORMAT;
+typedef struct tagDVTARGETDEVICE DVTARGETDEVICE;
+
+typedef struct tagFORMATETC FORMATETC;
+
+// Use WIN32_FIND_DATAW when you just need a forward declaration. Use
+// CHROME_WIN32_FIND_DATA when you need a concrete declaration to reserve
+// space.
+typedef struct _WIN32_FIND_DATAW WIN32_FIND_DATAW;
+typedef WIN32_FIND_DATAW WIN32_FIND_DATA;
+
 // Declare Chrome versions of some Windows structures. These are needed for
 // when we need a concrete type but don't want to pull in Windows.h. We can't
 // declare the Windows types so we declare our types and cast to the Windows
-// types in a few places.
+// types in a few places. The sizes must match the Windows types so we verify
+// that with static asserts in win_includes_unittest.cc.
 
 struct CHROME_SRWLOCK {
   PVOID Ptr;
@@ -138,6 +150,20 @@
   PVOID Ptr;
 };
 
+// _WIN32_FIND_DATAW is 592 bytes and the largest built-in type in it is a
+// DWORD. The buffer declaration guarantees the correct size and alignment.
+struct CHROME_WIN32_FIND_DATA {
+  DWORD buffer[592 / sizeof(DWORD)];
+};
+
+struct CHROME_FORMATETC {
+  CLIPFORMAT cfFormat;
+  /* [unique] */ DVTARGETDEVICE* ptd;
+  DWORD dwAspect;
+  LONG lindex;
+  DWORD tymed;
+};
+
 // Define some commonly used Windows constants. Note that the layout of these
 // macros - including internal spacing - must be 100% consistent with windows.h.
 
@@ -158,7 +184,9 @@
 #define ERROR_INVALID_HANDLE 6L
 #define ERROR_SHARING_VIOLATION 32L
 #define ERROR_LOCK_VIOLATION 33L
+#define ERROR_MORE_DATA 234L
 #define REG_BINARY ( 3ul )
+#define REG_NONE ( 0ul )
 
 #define STATUS_PENDING ((DWORD   )0x00000103L)
 #define STILL_ACTIVE STATUS_PENDING
@@ -178,6 +206,7 @@
 #define KEY_WOW64_64KEY (0x0100)
 #define KEY_WOW64_RES (0x0300)
 
+#define PROCESS_QUERY_INFORMATION (0x0400)
 #define READ_CONTROL (0x00020000L)
 #define SYNCHRONIZE (0x00100000L)
 
@@ -222,6 +251,7 @@
 #define WINBASEAPI DECLSPEC_IMPORT
 #define WINUSERAPI DECLSPEC_IMPORT
 #define WINAPI __stdcall
+#define APIENTRY WINAPI
 #define CALLBACK __stdcall
 
 // Needed for LockImpl.
diff --git a/build/android/adb_logcat_monitor.py b/build/android/adb_logcat_monitor.py
index a919722..de1573c 100755
--- a/build/android/adb_logcat_monitor.py
+++ b/build/android/adb_logcat_monitor.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/adb_logcat_printer.py b/build/android/adb_logcat_printer.py
index a715170..daa4ddb 100755
--- a/build/android/adb_logcat_printer.py
+++ b/build/android/adb_logcat_printer.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/asan_symbolize.py b/build/android/asan_symbolize.py
index 65850898..60d4d44d 100755
--- a/build/android/asan_symbolize.py
+++ b/build/android/asan_symbolize.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright 2013 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/diff_resource_sizes.py b/build/android/diff_resource_sizes.py
index eefb6cd..f9f7b8c 100755
--- a/build/android/diff_resource_sizes.py
+++ b/build/android/diff_resource_sizes.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2017 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/build/android/download_doclava.py b/build/android/download_doclava.py
index 1982fdb8..059d1cb 100755
--- a/build/android/download_doclava.py
+++ b/build/android/download_doclava.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2016 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/build/android/gradle/gn_to_cmake.py b/build/android/gradle/gn_to_cmake.py
index d3e80ae..7289825 100755
--- a/build/android/gradle/gn_to_cmake.py
+++ b/build/android/gradle/gn_to_cmake.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2016 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/build/android/gyp/create_apk_operations_script.py b/build/android/gyp/create_apk_operations_script.py
index 660567f0..82a6e5b 100755
--- a/build/android/gyp/create_apk_operations_script.py
+++ b/build/android/gyp/create_apk_operations_script.py
@@ -12,7 +12,7 @@
 from util import build_utils
 
 SCRIPT_TEMPLATE = string.Template("""\
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # This file was generated by build/android/gyp/create_apk_operations_script.py
 
diff --git a/build/android/gyp/create_bundle_wrapper_script.py b/build/android/gyp/create_bundle_wrapper_script.py
index 282e206..1bdb767 100755
--- a/build/android/gyp/create_bundle_wrapper_script.py
+++ b/build/android/gyp/create_bundle_wrapper_script.py
@@ -13,7 +13,7 @@
 from util import build_utils
 
 SCRIPT_TEMPLATE = string.Template("""\
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # This file was generated by build/android/gyp/create_bundle_wrapper_script.py
 
diff --git a/build/android/gyp/create_java_binary_script.py b/build/android/gyp/create_java_binary_script.py
index 5bc9d08..91fe600 100755
--- a/build/android/gyp/create_java_binary_script.py
+++ b/build/android/gyp/create_java_binary_script.py
@@ -21,7 +21,7 @@
 # to the directory that the script is written in and then, when run, must
 # recalculate the paths relative to the current directory.
 script_template = """\
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # This file was generated by build/android/gyp/create_java_binary_script.py
 
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index a79f144..4756d8ac 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -1763,7 +1763,6 @@
   if is_static_library_dex_provider_target:
     # Map classpath entries to configs that include them in their classpath.
     configs_by_classpath_entry = collections.defaultdict(list)
-    static_lib_jar_paths = {}
     for config_path, dep_config in (sorted(
         static_library_dependent_configs_by_path.items())):
       # For bundles, only the jar path and jni sources of the base module
@@ -1773,7 +1772,6 @@
       if dep_config['type'] == 'android_app_bundle':
         base_config = GetDepConfig(dep_config['base_module_config'])
       extra_main_r_text_files.append(base_config['r_text_path'])
-      static_lib_jar_paths[config_path] = base_config['device_jar_path']
       proguard_configs.extend(dep_config['proguard_all_configs'])
       extra_proguard_classpath_jars.extend(
           dep_config['proguard_classpath_jars'])
diff --git a/build/android/incremental_install/generate_android_manifest.py b/build/android/incremental_install/generate_android_manifest.py
index e069dab..8b938a3 100755
--- a/build/android/incremental_install/generate_android_manifest.py
+++ b/build/android/incremental_install/generate_android_manifest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright 2015 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/incremental_install/write_installer_json.py b/build/android/incremental_install/write_installer_json.py
index cf1d2d4..ce88e8a 100755
--- a/build/android/incremental_install/write_installer_json.py
+++ b/build/android/incremental_install/write_installer_json.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # Copyright 2017 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/lighttpd_server.py b/build/android/lighttpd_server.py
index 42fbcdb..e656e86 100755
--- a/build/android/lighttpd_server.py
+++ b/build/android/lighttpd_server.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/method_count.py b/build/android/method_count.py
index a39a390..80d0073 100755
--- a/build/android/method_count.py
+++ b/build/android/method_count.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env python3
 # Copyright 2015 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/build/android/native_flags/argcapture.py b/build/android/native_flags/argcapture.py
index 159b03ab..b0e2acd9 100755
--- a/build/android/native_flags/argcapture.py
+++ b/build/android/native_flags/argcapture.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2021 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/build/android/pylib/dex/dex_parser.py b/build/android/pylib/dex/dex_parser.py
index be5f1af..1ff8d25 100755
--- a/build/android/pylib/dex/dex_parser.py
+++ b/build/android/pylib/dex/dex_parser.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2019 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/build/android/pylib/results/presentation/standard_gtest_merge.py b/build/android/pylib/results/presentation/standard_gtest_merge.py
index 64f40e6..d458223 100755
--- a/build/android/pylib/results/presentation/standard_gtest_merge.py
+++ b/build/android/pylib/results/presentation/standard_gtest_merge.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env python3
 #
 # Copyright 2017 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/pylib/results/presentation/test_results_presentation.py b/build/android/pylib/results/presentation/test_results_presentation.py
index 2fd98b11..fc14b8b 100755
--- a/build/android/pylib/results/presentation/test_results_presentation.py
+++ b/build/android/pylib/results/presentation/test_results_presentation.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright 2017 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/pylib/symbols/apk_lib_dump.py b/build/android/pylib/symbols/apk_lib_dump.py
index 933a0ab..f40c758 100755
--- a/build/android/pylib/symbols/apk_lib_dump.py
+++ b/build/android/pylib/symbols/apk_lib_dump.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # Copyright 2018 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/update_deps/update_third_party_deps.py b/build/android/update_deps/update_third_party_deps.py
index 3a869c4..c03fec5d 100755
--- a/build/android/update_deps/update_third_party_deps.py
+++ b/build/android/update_deps/update_third_party_deps.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2016 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/build/chromeos/test_runner.py b/build/chromeos/test_runner.py
index 0caaace65..0e433775 100755
--- a/build/chromeos/test_runner.py
+++ b/build/chromeos/test_runner.py
@@ -241,6 +241,8 @@
       run_results.AddResult(suite_result)
       with open(self._test_launcher_summary_output, 'w') as f:
         json.dump(json_results.GenerateResultsDict([run_results]), f)
+      if self._rdb_client:
+        self._rdb_client.Post(self.suite_name, result, None, None, None)
 
   @staticmethod
   def get_artifacts(path):
@@ -452,8 +454,13 @@
         # inside as an RDB 'artifact'. (This could include system logs, screen
         # shots, etc.)
         artifacts = self.get_artifacts(test['outDir'])
-        self._rdb_client.Post(test['name'], result, duration_ms, error_log,
-                              artifacts)
+        self._rdb_client.Post(
+            test['name'],
+            result,
+            duration_ms,
+            error_log,
+            None,
+            artifacts=artifacts)
 
     if self._rdb_client and self._logs_dir:
       # Attach artifacts from the device that don't apply to a single test.
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 88de18f..f6b07a68 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -2358,9 +2358,6 @@
 
     # All configs using /DEBUG should include this:
     configs = [ ":win_pdbaltpath" ]
-
-    # TODO(crbug.com/1138553): Re-enable constructor homing on windows after
-    # libc++ fix is in.
   } else {
     cflags = []
     if (is_mac && enable_dsyms) {
@@ -2391,14 +2388,6 @@
       cflags += [ "-g2" ]
     }
 
-    # TODO(https://crbug.com/1050118): Investigate missing debug info on mac.
-    if (is_clang && !is_nacl && !use_xcode_clang && !is_apple) {
-      cflags += [
-        "-Xclang",
-        "-debug-info-kind=constructor",
-      ]
-    }
-
     if (is_apple) {
       swiftflags = [ "-g" ]
     }
@@ -2432,6 +2421,16 @@
       ldflags += [ "-Wl,--gdb-index" ]
     }
   }
+
+  # TODO(https://crbug.com/1050118): Investigate missing debug info on mac.
+  if (is_clang && !is_nacl && !use_xcode_clang && !is_apple) {
+    # Use constructor homing for debug info. This option reduces debug info
+    # by emitting class type info only when constructors are emitted.
+    cflags += [
+      "-Xclang",
+      "-fuse-ctor-homing",
+    ]
+  }
 }
 
 # Minimal symbols.
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 2d9a3c1..7036701 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-5.20210709.1.1
+5.20210709.2.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 2d9a3c1..7036701 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-5.20210709.1.1
+5.20210709.2.1
diff --git a/cc/trees/property_tree.h b/cc/trees/property_tree.h
index f5c08a5b2..e745252 100644
--- a/cc/trees/property_tree.h
+++ b/cc/trees/property_tree.h
@@ -74,11 +74,11 @@
   int Insert(const T& tree_node, int parent_id);
 
   T* Node(int i) {
-    DCHECK(i < static_cast<int>(nodes_.size()));
+    CHECK_LT(i, static_cast<int>(nodes_.size()));
     return i > kInvalidNodeId ? &nodes_[i] : nullptr;
   }
   const T* Node(int i) const {
-    DCHECK(i < static_cast<int>(nodes_.size()));
+    CHECK_LT(i, static_cast<int>(nodes_.size()));
     return i > kInvalidNodeId ? &nodes_[i] : nullptr;
   }
 
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 08999fee..363d43b 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -755,6 +755,7 @@
     "//chrome/android/features/autofill_assistant:jni_headers",
     "//chrome/android/features/autofill_assistant:jni_headers_public",
     "//chrome/android/features/keyboard_accessory:jni_headers",
+    "//chrome/browser/android/browserservices/metrics:jni_headers",
     "//chrome/browser/android/browserservices/verification:jni_headers",
     "//chrome/browser/commerce/merchant_viewer/android:jni_headers",
     "//chrome/browser/commerce/subscriptions/android:jni_headers",
@@ -3651,7 +3652,6 @@
     "java/src/org/chromium/chrome/browser/webapps/WebApkInstallService.java",
     "java/src/org/chromium/chrome/browser/webapps/WebApkInstaller.java",
     "java/src/org/chromium/chrome/browser/webapps/WebApkPostShareTargetNavigator.java",
-    "java/src/org/chromium/chrome/browser/webapps/WebApkUkmRecorder.java",
     "java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java",
     "java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java",
     "java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 107ea87..97620d4 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1138,7 +1138,6 @@
   "java/src/org/chromium/chrome/browser/tab/TabFavicon.java",
   "java/src/org/chromium/chrome/browser/tab/TabGestureStateListener.java",
   "java/src/org/chromium/chrome/browser/tab/TabHelpers.java",
-  "java/src/org/chromium/chrome/browser/tab/TabIdManager.java",
   "java/src/org/chromium/chrome/browser/tab/TabImpl.java",
   "java/src/org/chromium/chrome/browser/tab/TabImportanceManager.java",
   "java/src/org/chromium/chrome/browser/tab/TabParentIntent.java",
@@ -1236,7 +1235,6 @@
   "java/src/org/chromium/chrome/browser/webapps/WebApkServiceClient.java",
   "java/src/org/chromium/chrome/browser/webapps/WebApkShareTargetUtil.java",
   "java/src/org/chromium/chrome/browser/webapps/WebApkSplashNetworkErrorObserver.java",
-  "java/src/org/chromium/chrome/browser/webapps/WebApkUkmRecorder.java",
   "java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java",
   "java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java",
   "java/src/org/chromium/chrome/browser/webapps/WebApkUpdateReportAbuseDialog.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index aa215a9..002c203 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -335,6 +335,7 @@
   "javatests/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessorTest.java",
   "javatests/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionUnitTest.java",
   "javatests/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessorUnitTest.java",
+  "javatests/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java",
   "javatests/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesTest.java",
   "javatests/src/org/chromium/chrome/browser/omnibox/suggestions/tiles/TileSuggestionProcessorUnitTest.java",
   "javatests/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchConsentUiRenderTest.java",
@@ -541,7 +542,6 @@
   "javatests/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateTest.java",
   "javatests/src/org/chromium/chrome/browser/tab/RepostFormWarningTest.java",
   "javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java",
-  "javatests/src/org/chromium/chrome/browser/tab/TabIdManagerTest.java",
   "javatests/src/org/chromium/chrome/browser/tab/TabStateTest.java",
   "javatests/src/org/chromium/chrome/browser/tab/TabUmaTest.java",
   "javatests/src/org/chromium/chrome/browser/tab/TabViewManagerTest.java",
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index 401cd02..de7f192 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -85,6 +85,7 @@
     "java/res/layout/tab_selection_editor_toolbar.xml",
     "java/res/layout/tab_strip_item.xml",
     "java/res/layout/tasks_view_layout.xml",
+    "java/res/values-night/dimens.xml",
     "java/res/values/colors.xml",
     "java/res/values/dimens.xml",
     "java/res/values/drawables.xml",
@@ -177,7 +178,7 @@
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageService.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java",
-    "java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiColorProvider.java",
+    "java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarController.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/BaselineTabSuggestionProvider.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/StaleTabSuggestionProvider.java",
diff --git a/chrome/android/features/tab_ui/java/res/values-night/dimens.xml b/chrome/android/features/tab_ui/java/res/values-night/dimens.xml
new file mode 100644
index 0000000..087a641
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/res/values-night/dimens.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+<resources>
+    <integer name="tab_thumbnail_placeholder_color_alpha">38</integer>
+    <integer name="tab_thumbnail_placeholder_selected_color_alpha">255</integer>
+</resources>
diff --git a/chrome/android/features/tab_ui/java/res/values/colors.xml b/chrome/android/features/tab_ui/java/res/values/colors.xml
index 423e7c1..ea75dd99 100644
--- a/chrome/android/features/tab_ui/java/res/values/colors.xml
+++ b/chrome/android/features/tab_ui/java/res/values/colors.xml
@@ -54,4 +54,10 @@
 
     <color name="incognito_tab_title_color">@color/baseline_neutral_100</color>
     <color name="incognito_tab_title_selected_color">@color/baseline_primary_800</color>
+
+    <color name="incognito_tab_thumbnail_placeholder_color">@color/baseline_neutral_variant_200_alpha_15</color>
+    <color name="incognito_tab_thumbnail_placeholder_selected_color">@color/baseline_primary_100</color>
+
+    <color name="incognito_tab_tile_number_color">@color/baseline_neutral_100</color>
+    <color name="incognito_tab_tile_number_selected_color">@color/baseline_primary_900</color>
 </resources>
diff --git a/chrome/android/features/tab_ui/java/res/values/dimens.xml b/chrome/android/features/tab_ui/java/res/values/dimens.xml
index 7e892a5..21713600 100644
--- a/chrome/android/features/tab_ui/java/res/values/dimens.xml
+++ b/chrome/android/features/tab_ui/java/res/values/dimens.xml
@@ -49,4 +49,7 @@
 
     <!-- Elevation -->
     <dimen name="tab_bg_elevation">@dimen/default_elevation_4</dimen>
+
+    <integer name="tab_thumbnail_placeholder_color_alpha">255</integer>
+    <integer name="tab_thumbnail_placeholder_selected_color_alpha">97</integer>
 </resources>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardView.java
index 551a83a..e7d19f9 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardView.java
@@ -151,7 +151,7 @@
      * @param isIncognito Whether the resource is used for incognito mode.
      */
     private void setBackground(boolean isIncognito) {
-        setBackgroundResource(TabUiColorProvider.getMessageCardBackgroundResourceId(isIncognito));
+        setBackgroundResource(TabUiThemeProvider.getMessageCardBackgroundResourceId(isIncognito));
     }
 
     /**
@@ -160,7 +160,7 @@
      */
     private void setDescriptionTextAppearance(boolean isIncognito) {
         ApiCompatibilityUtils.setTextAppearance(mDescription,
-                TabUiColorProvider.getMessageCardDescriptionTextAppearance(isIncognito));
+                TabUiThemeProvider.getMessageCardDescriptionTextAppearance(isIncognito));
     }
 
     /**
@@ -169,7 +169,7 @@
      */
     private void setActionButtonTextAppearance(boolean isIncognito) {
         ApiCompatibilityUtils.setTextAppearance(mActionButton,
-                TabUiColorProvider.getMessageCardActionButtonTextAppearance(isIncognito));
+                TabUiThemeProvider.getMessageCardActionButtonTextAppearance(isIncognito));
     }
 
     /**
@@ -178,7 +178,7 @@
      */
     private void setCloseButtonTint(boolean isIncognito) {
         ApiCompatibilityUtils.setImageTintList(mCloseButton,
-                TabUiColorProvider.getMessageCardCloseButtonTintList(
+                TabUiThemeProvider.getMessageCardCloseButtonTintList(
                         mCloseButton.getContext(), isIncognito));
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
index 83d65f9..dd0ea14 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
@@ -50,8 +50,11 @@
     private final int mThumbnailHeight;
     private final Paint mEmptyThumbnailPaint;
     private final Paint mThumbnailFramePaint;
+    private final Paint mThumbnailBasePaint;
     private final Paint mTextPaint;
     private final Paint mFaviconBackgroundPaint;
+    private final Paint mSelectedEmptyThumbnailPaint;
+    private final Paint mSelectedTextPaint;
     private final int mFaviconBackgroundPaintColor;
     private final List<Rect> mFaviconRects = new ArrayList<>(4);
     private final List<RectF> mThumbnailRects = new ArrayList<>(4);
@@ -64,6 +67,7 @@
         private final Callback<Bitmap> mFinalCallback;
         private final boolean mForceUpdate;
         private final boolean mWriteToCache;
+        private final boolean mIsTabSelected;
         private final List<PseudoTab> mTabs = new ArrayList<>(4);
         private final AtomicInteger mThumbnailsToFetch = new AtomicInteger();
 
@@ -72,14 +76,17 @@
         private String mText;
 
         /**
+         * Fetcher that get the thumbnail drawable depending on if the tab is selected.
          * @see TabContentManager#getTabThumbnailWithCallback
+         * @param isTabSelected Whether the thumbnail is for a currently selected tab.
          */
         MultiThumbnailFetcher(PseudoTab initialTab, Callback<Bitmap> finalCallback,
-                boolean forceUpdate, boolean writeToCache) {
+                boolean forceUpdate, boolean writeToCache, boolean isTabSelected) {
             mFinalCallback = finalCallback;
             mInitialTab = initialTab;
             mForceUpdate = forceUpdate;
             mWriteToCache = writeToCache;
+            mIsTabSelected = isTabSelected;
         }
 
         private void initializeAndStartFetching(PseudoTab tab) {
@@ -142,35 +149,42 @@
                     drawThumbnailBitmapOnCanvasWithFrame(null, i);
                     if (mText != null && i == 3) {
                         // Draw the text exactly centered on the thumbnail rect.
+                        Paint textPaint = mIsTabSelected ? mSelectedTextPaint : mTextPaint;
                         mCanvas.drawText(mText,
                                 (mThumbnailRects.get(i).left + mThumbnailRects.get(i).right) / 2,
                                 (mThumbnailRects.get(i).top + mThumbnailRects.get(i).bottom) / 2
                                         - ((mTextPaint.descent() + mTextPaint.ascent()) / 2),
-                                mTextPaint);
+                                textPaint);
                     }
                 }
             }
         }
 
         private void drawThumbnailBitmapOnCanvasWithFrame(Bitmap thumbnail, int index) {
-            // Draw the rounded rect. If Bitmap is not null, this is used for XferMode.
-            mCanvas.drawRoundRect(
-                    mThumbnailRects.get(index), mRadius, mRadius, mEmptyThumbnailPaint);
+            if (thumbnail == null) {
+                Paint emptyThumbnailPaint =
+                        mIsTabSelected ? mSelectedEmptyThumbnailPaint : mEmptyThumbnailPaint;
+                mCanvas.drawRoundRect(
+                        mThumbnailRects.get(index), mRadius, mRadius, emptyThumbnailPaint);
+                return;
+            }
 
-            if (thumbnail == null) return;
+            // Draw the base paint first and set the base for thumbnail to draw. Setting the xfer
+            // mode as SRC_OVER so the thumbnail can be drawn on top of this paint. See
+            // https://crbug.com/1227619.
+            mThumbnailBasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
+            mCanvas.drawRoundRect(
+                    mThumbnailRects.get(index), mRadius, mRadius, mThumbnailBasePaint);
 
             thumbnail =
                     Bitmap.createScaledBitmap(thumbnail, (int) mThumbnailRects.get(index).width(),
                             (int) mThumbnailRects.get(index).height(), true);
-
-            mEmptyThumbnailPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+            mThumbnailBasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
             mCanvas.drawBitmap(thumbnail,
                     new Rect(0, 0, thumbnail.getWidth(), thumbnail.getHeight()),
-                    mThumbnailRects.get(index), mEmptyThumbnailPaint);
+                    mThumbnailRects.get(index), mThumbnailBasePaint);
             thumbnail.recycle();
 
-            mEmptyThumbnailPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
-
             mCanvas.drawRoundRect(
                     mThumbnailRects.get(index), mRadius, mRadius, mThumbnailFramePaint);
         }
@@ -217,9 +231,18 @@
         // Initialize Paints to use.
         mEmptyThumbnailPaint = new Paint();
         mEmptyThumbnailPaint.setStyle(Paint.Style.FILL);
-        mEmptyThumbnailPaint.setColor(ApiCompatibilityUtils.getColor(
-                resource, R.color.tab_list_mini_card_default_background_color));
         mEmptyThumbnailPaint.setAntiAlias(true);
+        mEmptyThumbnailPaint.setColor(
+                TabUiThemeProvider.getMiniThumbnailPlaceHolderColor(context, false, false));
+
+        mSelectedEmptyThumbnailPaint = new Paint(mEmptyThumbnailPaint);
+        mSelectedEmptyThumbnailPaint.setColor(
+                TabUiThemeProvider.getMiniThumbnailPlaceHolderColor(context, false, true));
+
+        // Paint used to set base for thumbnails, in case mEmptyThumbnailPaint has transparency.
+        mThumbnailBasePaint = new Paint(mEmptyThumbnailPaint);
+        mThumbnailBasePaint.setColor(Color.BLACK);
+        mThumbnailBasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
 
         mThumbnailFramePaint = new Paint();
         mThumbnailFramePaint.setStyle(Paint.Style.STROKE);
@@ -236,7 +259,11 @@
         mTextPaint.setFakeBoldText(true);
         mTextPaint.setAntiAlias(true);
         mTextPaint.setTextAlign(Paint.Align.CENTER);
-        mTextPaint.setColor(ApiCompatibilityUtils.getColor(resource, R.color.default_text_color));
+        mTextPaint.setColor(TabUiThemeProvider.getTabGroupNumberTextColor(context, false, false));
+
+        mSelectedTextPaint = new Paint(mTextPaint);
+        mSelectedTextPaint.setColor(
+                TabUiThemeProvider.getTabGroupNumberTextColor(context, false, true));
 
         mFaviconBackgroundPaintColor =
                 ApiCompatibilityUtils.getColor(resource, R.color.favicon_background_color);
@@ -297,14 +324,20 @@
             @Override
             public void onTabModelSelected(TabModel newModel, TabModel oldModel) {
                 boolean isIncognito = newModel.isIncognito();
-                mEmptyThumbnailPaint.setColor(
-                        TabUiColorProvider.getMiniThumbnailPlaceHolderColor(context, isIncognito));
-                mThumbnailFramePaint.setColor(
-                        TabUiColorProvider.getMiniThumbnailFrameColor(context, isIncognito));
+                mEmptyThumbnailPaint.setColor(TabUiThemeProvider.getMiniThumbnailPlaceHolderColor(
+                        context, isIncognito, false));
                 mTextPaint.setColor(
-                        TabUiColorProvider.getTabGroupNumberTextColor(context, isIncognito));
+                        TabUiThemeProvider.getTabGroupNumberTextColor(context, isIncognito, false));
+                mThumbnailFramePaint.setColor(
+                        TabUiThemeProvider.getMiniThumbnailFrameColor(context, isIncognito));
                 mFaviconBackgroundPaint.setColor(
-                        TabUiColorProvider.getFaviconBackgroundColor(context, isIncognito));
+                        TabUiThemeProvider.getFaviconBackgroundColor(context, isIncognito));
+
+                mSelectedEmptyThumbnailPaint.setColor(
+                        TabUiThemeProvider.getMiniThumbnailPlaceHolderColor(
+                                context, isIncognito, true));
+                mSelectedTextPaint.setColor(
+                        TabUiThemeProvider.getTabGroupNumberTextColor(context, isIncognito, true));
             }
         };
         mTabModelSelector.addObserver(mTabModelSelectorObserver);
@@ -333,7 +366,8 @@
                     tabId, finalCallback, forceUpdate, writeToCache);
             return;
         }
-
-        new MultiThumbnailFetcher(tab, finalCallback, forceUpdate, writeToCache).fetch();
+        boolean isSelected = tabId == mTabModelSelector.getCurrentTabId();
+        new MultiThumbnailFetcher(tab, finalCallback, forceUpdate, writeToCache, isSelected)
+                .fetch();
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/NewTabTileView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/NewTabTileView.java
index f9a08d0..7081842 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/NewTabTileView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/NewTabTileView.java
@@ -65,9 +65,9 @@
      */
     void updateColor(boolean isIncognito) {
         ViewCompat.setBackgroundTintList(this,
-                TabUiColorProvider.getHoveredCardBackgroundTintList(getContext(), isIncognito));
+                TabUiThemeProvider.getHoveredCardBackgroundTintList(getContext(), isIncognito));
 
         ApiCompatibilityUtils.setImageTintList(mActionButtonView,
-                TabUiColorProvider.getNewTabTilePlusTintList(getContext(), isIncognito));
+                TabUiThemeProvider.getNewTabTilePlusTintList(getContext(), isIncognito));
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
index e603e59..8dac4fb 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
@@ -6,6 +6,7 @@
 
 import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.CardProperties.CARD_ALPHA;
 
+import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
@@ -113,6 +114,7 @@
             if (CachedFeatureFlags.isEnabled(ChromeFeatureList.THEME_REFACTOR_ANDROID)) {
                 updateColor(view, model.get(TabProperties.IS_INCOGNITO),
                         model.get(TabProperties.IS_SELECTED));
+                updateThumbnail(view, model);
             } else {
                 int selectedTabBackground =
                         model.get(TabProperties.SELECTED_TAB_BACKGROUND_DRAWABLE_ID);
@@ -403,23 +405,28 @@
 
         cardView.getBackground().mutate();
         ViewCompat.setBackgroundTintList(cardView,
-                TabUiColorProvider.getCardViewTintList(
+                TabUiThemeProvider.getCardViewTintList(
                         cardView.getContext(), isIncognito, isSelected));
 
         dividerView.setBackgroundColor(
-                TabUiColorProvider.getDividerColor(dividerView.getContext(), isIncognito));
+                TabUiThemeProvider.getDividerColor(dividerView.getContext(), isIncognito));
 
-        titleView.setTextColor(TabUiColorProvider.getTitleTextColor(
+        titleView.setTextColor(TabUiThemeProvider.getTitleTextColor(
                 titleView.getContext(), isIncognito, isSelected));
 
         if (thumbnail.getDrawable() == null) {
             thumbnail.setImageResource(
-                    TabUiColorProvider.getThumbnailPlaceHolderColorResource(isIncognito));
+                    TabUiThemeProvider.getThumbnailPlaceHolderColorResource(isIncognito));
+            if (CachedFeatureFlags.isEnabled(ChromeFeatureList.THEME_REFACTOR_ANDROID)) {
+                thumbnail.setImageTintList(
+                        ColorStateList.valueOf(TabUiThemeProvider.getMiniThumbnailPlaceHolderColor(
+                                backgroundView.getContext(), isIncognito, isSelected)));
+            }
         }
 
         if (TabUiFeatureUtilities.isTabGroupsAndroidEnabled(rootView.getContext())) {
             ViewCompat.setBackgroundTintList(backgroundView,
-                    TabUiColorProvider.getHoveredCardBackgroundTintList(
+                    TabUiThemeProvider.getHoveredCardBackgroundTintList(
                             backgroundView.getContext(), isIncognito));
         }
     }
@@ -428,7 +435,7 @@
             ViewLookupCachingFrameLayout rootView, boolean isIncognito, boolean isSelected) {
         ImageView actionButton = (ImageView) rootView.fastFindViewById(R.id.action_button);
         ApiCompatibilityUtils.setImageTintList(actionButton,
-                TabUiColorProvider.getActionButtonTintList(
+                TabUiThemeProvider.getActionButtonTintList(
                         actionButton.getContext(), isIncognito, isSelected));
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiColorProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java
similarity index 82%
rename from chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiColorProvider.java
rename to chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java
index 8828958..f7292c9 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiColorProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java
@@ -21,10 +21,10 @@
 import org.chromium.chrome.tab_ui.R;
 
 /**
- * Color utility class for a Tab Grid card.
+ * Utility class that provides theme related attributes for Tab UI.
  */
-public class TabUiColorProvider {
-    private static final String TAG = "TabUiColorProvider";
+public class TabUiThemeProvider {
+    private static final String TAG = "TabUiThemeProvider";
     /**
      * Returns the {@link ColorStateList} to use for the tab grid card view background based on
      * incognito mode.
@@ -77,13 +77,27 @@
      *
      * @param context {@link Context} used to retrieve color.
      * @param isIncognito Whether the color is used for incognito mode.
+     * @param isSelected Whether the tab is currently selected.
      * @return The text color for the number used on the tab group cards.
      */
     @ColorInt
-    public static int getTabGroupNumberTextColor(Context context, boolean isIncognito) {
-        return ApiCompatibilityUtils.getColor(context.getResources(),
-                isIncognito ? R.color.tab_group_number_text_color_incognito
-                            : R.color.tab_group_number_text_color);
+    public static int getTabGroupNumberTextColor(
+            Context context, boolean isIncognito, boolean isSelected) {
+        if (!themeRefactorEnabled()) {
+            return ApiCompatibilityUtils.getColor(context.getResources(),
+                    isIncognito ? R.color.tab_group_number_text_color_incognito
+                                : R.color.tab_group_number_text_color);
+        }
+        if (isIncognito) {
+            @ColorRes
+            int colorRes = isSelected ? R.color.incognito_tab_tile_number_selected_color
+                                      : R.color.incognito_tab_tile_number_color;
+            return ApiCompatibilityUtils.getColor(context.getResources(), colorRes);
+        } else {
+            return isSelected
+                    ? MaterialColors.getColor(context, R.attr.colorOnPrimaryContainer, TAG)
+                    : MaterialColors.getColor(context, R.attr.colorOnSurface, TAG);
+        }
     }
 
     /**
@@ -188,13 +202,34 @@
      *
      * @param context {@link Context} used to retrieve color.
      * @param isIncognito Whether the color is used for incognito mode.
+     * @param isSelected Whether the tab is currently selected.
      * @return The mini-thumbnail placeholder color.
      */
     @ColorInt
-    public static int getMiniThumbnailPlaceHolderColor(Context context, boolean isIncognito) {
-        return ApiCompatibilityUtils.getColor(context.getResources(),
-                isIncognito ? R.color.tab_list_mini_card_default_background_color_incognito
-                            : R.color.tab_list_mini_card_default_background_color);
+    public static int getMiniThumbnailPlaceHolderColor(
+            Context context, boolean isIncognito, boolean isSelected) {
+        if (!themeRefactorEnabled()) {
+            return ApiCompatibilityUtils.getColor(context.getResources(),
+                    isIncognito ? R.color.tab_list_mini_card_default_background_color_incognito
+                                : R.color.tab_list_mini_card_default_background_color);
+        }
+
+        if (isIncognito) {
+            @ColorRes
+            int colorRes = isSelected ? R.color.incognito_tab_thumbnail_placeholder_selected_color
+                                      : R.color.incognito_tab_thumbnail_placeholder_color;
+            return ApiCompatibilityUtils.getColor(context.getResources(), colorRes);
+        } else {
+            int alpha = context.getResources().getInteger(isSelected
+                            ? R.integer.tab_thumbnail_placeholder_selected_color_alpha
+                            : R.integer.tab_thumbnail_placeholder_color_alpha);
+            @ColorInt
+            int baseColor = isSelected
+                    ? MaterialColors.getColor(context, R.attr.colorPrimaryContainer, TAG)
+                    // TODO (crrev.com/c/2994242): Change light mode to Surface1.
+                    : MaterialColors.getColor(context, R.attr.colorOnSurfaceVariant, TAG);
+            return MaterialColors.compositeARGBWithAlpha(baseColor, alpha);
+        }
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index 6a0fa03a..6b3eb7d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -108,6 +108,7 @@
 import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncherImpl;
 import org.chromium.chrome.browser.firstrun.ForcedSigninProcessor;
 import org.chromium.chrome.browser.flags.ActivityType;
+import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSessionState;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
@@ -389,6 +390,18 @@
     }
 
     @Override
+    protected void onPreCreate() {
+        CachedFeatureFlags.onStartOrResumeCheckpoint();
+        super.onPreCreate();
+    }
+
+    @Override
+    protected void onAbortCreate() {
+        super.onAbortCreate();
+        CachedFeatureFlags.onPauseCheckpoint();
+    }
+
+    @Override
     protected ActivityWindowAndroid createWindowAndroid() {
         return new ChromeWindow(/* activity= */ this, mActivityTabProvider,
                 mCompositorViewHolderSupplier, getModalDialogManagerSupplier(),
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
index b80068f..61a13b9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
@@ -26,7 +26,6 @@
 import org.chromium.chrome.features.start_surface.StartSurfaceConfiguration;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -42,10 +41,13 @@
      * {@link #cacheMinimalBrowserFlags()}.
      */
     private static final List<CachedFieldTrialParameter> MINIMAL_BROWSER_FIELD_TRIALS =
-            Arrays.asList(
+            new ArrayList<CachedFieldTrialParameter>() {
+                {
                     // This is used by CustomTabsConnection implementation, which does not
                     // necessarily start chrome.
-                    CustomTabActivity.EXPERIMENTS_FOR_AGSA_PARAMS);
+                    add(CustomTabActivity.EXPERIMENTS_FOR_AGSA_PARAMS);
+                }
+            };
 
     /**
      * @return The {@link ChromeCachedFlags} singleton.
@@ -63,96 +65,101 @@
         if (mIsFinishedCachingNativeFlags) return;
         FirstRunUtils.cacheFirstRunPrefs();
 
-        // clang-format off
-        List<String> featuresToCache = Arrays.asList(
-                ChromeFeatureList.ANDROID_PARTNER_CUSTOMIZATION_PHENOTYPE,
-                ChromeFeatureList.APP_MENU_MOBILE_SITE_OPTION,
-                ChromeFeatureList.APP_TO_WEB_ATTRIBUTION,
-                ChromeFeatureList.BOOKMARK_BOTTOM_SHEET,
-                ChromeFeatureList.CCT_INCOGNITO,
-                ChromeFeatureList.CCT_INCOGNITO_AVAILABLE_TO_THIRD_PARTY,
-                ChromeFeatureList.CCT_REMOVE_REMOTE_VIEW_IDS,
-                ChromeFeatureList.CLIPBOARD_SUGGESTION_CONTENT_HIDDEN,
-                ChromeFeatureList.CLOSE_TAB_SUGGESTIONS,
-                ChromeFeatureList.CRITICAL_PERSISTED_TAB_DATA,
-                ChromeFeatureList.COMMAND_LINE_ON_NON_ROOTED,
-                ChromeFeatureList.CONDITIONAL_TAB_STRIP_ANDROID,
-                ChromeFeatureList.DOWNLOADS_AUTO_RESUMPTION_NATIVE,
-                ChromeFeatureList.DYNAMIC_COLOR_ANDROID,
-                ChromeFeatureList.EARLY_LIBRARY_LOAD,
-                ChromeFeatureList.IMMERSIVE_UI_MODE,
-                ChromeFeatureList.INSTANT_START,
-                ChromeFeatureList.INTEREST_FEED_V2,
-                ChromeFeatureList.LENS_CAMERA_ASSISTED_SEARCH,
-                ChromeFeatureList.NEW_WINDOW_APP_MENU,
-                ChromeFeatureList.OFFLINE_MEASUREMENTS_BACKGROUND_TASK,
-                ChromeFeatureList.OPTIMIZATION_GUIDE_PUSH_NOTIFICATIONS,
-                ChromeFeatureList.PAINT_PREVIEW_DEMO,
-                ChromeFeatureList.PAINT_PREVIEW_SHOW_ON_STARTUP,
-                ChromeFeatureList.PRIORITIZE_BOOTSTRAP_TASKS,
-                ChromeFeatureList.READ_LATER,
-                ChromeFeatureList.START_SURFACE_ANDROID,
-                ChromeFeatureList.STORE_HOURS,
-                ChromeFeatureList.SWAP_PIXEL_FORMAT_TO_FIX_CONVERT_FROM_TRANSLUCENT,
-                ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID,
-                ChromeFeatureList.TAB_GROUPS_ANDROID,
-                ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID,
-                ChromeFeatureList.TAB_TO_GTS_ANIMATION,
-                ChromeFeatureList.THEME_REFACTOR_ANDROID,
-                ChromeFeatureList.TOOLBAR_USE_HARDWARE_BITMAP_DRAW,
-                ChromeFeatureList.USE_CHIME_ANDROID_SDK);
-        // clang-format on
+        // Workaround for crbug.com/1223545: Do not use Arrays.asList().
+        List<String> featuresToCache = new ArrayList<String>() {
+            {
+                add(ChromeFeatureList.ANDROID_PARTNER_CUSTOMIZATION_PHENOTYPE);
+                add(ChromeFeatureList.APP_MENU_MOBILE_SITE_OPTION);
+                add(ChromeFeatureList.APP_TO_WEB_ATTRIBUTION);
+                add(ChromeFeatureList.BOOKMARK_BOTTOM_SHEET);
+                add(ChromeFeatureList.CCT_INCOGNITO);
+                add(ChromeFeatureList.CCT_INCOGNITO_AVAILABLE_TO_THIRD_PARTY);
+                add(ChromeFeatureList.CCT_REMOVE_REMOTE_VIEW_IDS);
+                add(ChromeFeatureList.CLIPBOARD_SUGGESTION_CONTENT_HIDDEN);
+                add(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS);
+                add(ChromeFeatureList.CRITICAL_PERSISTED_TAB_DATA);
+                add(ChromeFeatureList.COMMAND_LINE_ON_NON_ROOTED);
+                add(ChromeFeatureList.CONDITIONAL_TAB_STRIP_ANDROID);
+                add(ChromeFeatureList.DOWNLOADS_AUTO_RESUMPTION_NATIVE);
+                add(ChromeFeatureList.DYNAMIC_COLOR_ANDROID);
+                add(ChromeFeatureList.EARLY_LIBRARY_LOAD);
+                add(ChromeFeatureList.IMMERSIVE_UI_MODE);
+                add(ChromeFeatureList.INSTANT_START);
+                add(ChromeFeatureList.INTEREST_FEED_V2);
+                add(ChromeFeatureList.LENS_CAMERA_ASSISTED_SEARCH);
+                add(ChromeFeatureList.NEW_WINDOW_APP_MENU);
+                add(ChromeFeatureList.OFFLINE_MEASUREMENTS_BACKGROUND_TASK);
+                add(ChromeFeatureList.OPTIMIZATION_GUIDE_PUSH_NOTIFICATIONS);
+                add(ChromeFeatureList.PAINT_PREVIEW_DEMO);
+                add(ChromeFeatureList.PAINT_PREVIEW_SHOW_ON_STARTUP);
+                add(ChromeFeatureList.PRIORITIZE_BOOTSTRAP_TASKS);
+                add(ChromeFeatureList.READ_LATER);
+                add(ChromeFeatureList.START_SURFACE_ANDROID);
+                add(ChromeFeatureList.STORE_HOURS);
+                add(ChromeFeatureList.SWAP_PIXEL_FORMAT_TO_FIX_CONVERT_FROM_TRANSLUCENT);
+                add(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID);
+                add(ChromeFeatureList.TAB_GROUPS_ANDROID);
+                add(ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID);
+                add(ChromeFeatureList.TAB_TO_GTS_ANIMATION);
+                add(ChromeFeatureList.THEME_REFACTOR_ANDROID);
+                add(ChromeFeatureList.TOOLBAR_USE_HARDWARE_BITMAP_DRAW);
+                add(ChromeFeatureList.USE_CHIME_ANDROID_SDK);
+            }
+        };
         CachedFeatureFlags.cacheNativeFlags(featuresToCache);
         CachedFeatureFlags.cacheAdditionalNativeFlags();
 
-        // clang-format off
-        List<CachedFieldTrialParameter> fieldTrialsToCache = Arrays.asList(
-                ChimeFeatures.ALWAYS_REGISTER,
-                ConditionalTabStripUtils.CONDITIONAL_TAB_STRIP_INFOBAR_LIMIT,
-                ConditionalTabStripUtils.CONDITIONAL_TAB_STRIP_INFOBAR_PERIOD,
-                ConditionalTabStripUtils.CONDITIONAL_TAB_STRIP_SESSION_TIME_MS,
-                LensFeature.DISABLE_LENS_CAMERA_ASSISTED_SEARCH_ON_INCOGNITO,
-                LensFeature.ENABLE_LENS_CAMERA_ASSISTED_SEARCH_ON_LOW_END_DEVICE,
-                LensFeature.ENABLE_LENS_CAMERA_ASSISTED_SEARCH_ON_TABLET,
-                LensFeature.SEARCH_BOX_START_VARIANT_LENS_CAMERA_ASSISTED_SEARCH,
-                LensFeature.MIN_AGSA_VERSION_LENS_CAMERA_ASSISTED_SEARCH,
-                MerchantViewerConfig.DEFAULT_TRUST_SIGNALS_MESSAGE_DELAY,
-                MerchantViewerConfig.TRUST_SIGNALS_MESSAGE_USE_RATING_BAR,
-                MerchantViewerConfig.TRUST_SIGNALS_MESSAGE_WINDOW_DURATION_SECONDS,
-                MerchantViewerConfig.TRUST_SIGNALS_SHEET_USE_PAGE_TITLE,
-                PageAnnotationsServiceConfig.PAGE_ANNOTATIONS_BASE_URL,
-                ReturnToChromeExperimentsUtil.TAB_SWITCHER_ON_RETURN_MS,
-                StartSurfaceConfiguration.HOME_BUTTON_ON_GRID_TAB_SWITCHER,
-                StartSurfaceConfiguration.NEW_SURFACE_FROM_HOME_BUTTON,
-                StartSurfaceConfiguration.OMNIBOX_FOCUSED_ON_NEW_TAB,
-                StartSurfaceConfiguration.SHOW_TABS_IN_MRU_ORDER,
-                StartSurfaceConfiguration.START_SURFACE_EXCLUDE_MV_TILES,
-                StartSurfaceConfiguration.START_SURFACE_HIDE_INCOGNITO_SWITCH_NO_TAB,
-                StartSurfaceConfiguration.START_SURFACE_LAST_ACTIVE_TAB_ONLY,
-                StartSurfaceConfiguration.START_SURFACE_OPEN_NTP_INSTEAD_OF_START,
-                StartSurfaceConfiguration.SHOW_NTP_TILES_ON_OMNIBOX,
-                StartSurfaceConfiguration.START_SURFACE_VARIATION,
-                StartSurfaceConfiguration.SUPPORT_ACCESSIBILITY,
-                StartupPaintPreviewHelper.ACCESSIBILITY_SUPPORT_PARAM,
-                CommerceSubscriptionsServiceConfig.STALE_TAB_LOWER_BOUND_SECONDS,
-                CommerceSubscriptionsServiceConfig.SUBSCRIPTIONS_SERVICE_BASE_URL,
-                PriceTrackingUtilities.ENABLE_PRICE_NOTIFICATION,
-                PriceTrackingUtilities.ENABLE_PRICE_TRACKING,
-                TabContentManager.ALLOW_TO_REFETCH_TAB_THUMBNAIL_VARIATION,
-                TabUiFeatureUtilities.ENABLE_LAUNCH_BUG_FIX,
-                TabUiFeatureUtilities.ENABLE_LAUNCH_POLISH,
-                TabUiFeatureUtilities.ENABLE_SEARCH_CHIP,
-                TabUiFeatureUtilities.ENABLE_SEARCH_CHIP_ADAPTIVE,
-                TabUiFeatureUtilities.ENABLE_TAB_GROUP_AUTO_CREATION,
-                TabUiFeatureUtilities.ZOOMING_MIN_MEMORY,
-                TabUiFeatureUtilities.ZOOMING_MIN_SDK,
-                TabUiFeatureUtilities.SKIP_SLOW_ZOOMING,
-                TabUiFeatureUtilities.TAB_GRID_LAYOUT_ANDROID_NEW_TAB_TILE,
-                TabUiFeatureUtilities.THUMBNAIL_ASPECT_RATIO);
-        // clang-format on
+        List<CachedFieldTrialParameter> fieldTrialsToCache =
+                new ArrayList<CachedFieldTrialParameter>() {
+                    {
+                        add(ChimeFeatures.ALWAYS_REGISTER);
+                        add(ConditionalTabStripUtils.CONDITIONAL_TAB_STRIP_INFOBAR_LIMIT);
+                        add(ConditionalTabStripUtils.CONDITIONAL_TAB_STRIP_INFOBAR_PERIOD);
+                        add(ConditionalTabStripUtils.CONDITIONAL_TAB_STRIP_SESSION_TIME_MS);
+                        add(LensFeature.DISABLE_LENS_CAMERA_ASSISTED_SEARCH_ON_INCOGNITO);
+                        add(LensFeature.ENABLE_LENS_CAMERA_ASSISTED_SEARCH_ON_LOW_END_DEVICE);
+                        add(LensFeature.ENABLE_LENS_CAMERA_ASSISTED_SEARCH_ON_TABLET);
+                        add(LensFeature.SEARCH_BOX_START_VARIANT_LENS_CAMERA_ASSISTED_SEARCH);
+                        add(LensFeature.MIN_AGSA_VERSION_LENS_CAMERA_ASSISTED_SEARCH);
+                        add(MerchantViewerConfig.DEFAULT_TRUST_SIGNALS_MESSAGE_DELAY);
+                        add(MerchantViewerConfig.TRUST_SIGNALS_MESSAGE_USE_RATING_BAR);
+                        add(MerchantViewerConfig.TRUST_SIGNALS_MESSAGE_WINDOW_DURATION_SECONDS);
+                        add(MerchantViewerConfig.TRUST_SIGNALS_SHEET_USE_PAGE_TITLE);
+                        add(PageAnnotationsServiceConfig.PAGE_ANNOTATIONS_BASE_URL);
+                        add(ReturnToChromeExperimentsUtil.TAB_SWITCHER_ON_RETURN_MS);
+                        add(StartSurfaceConfiguration.HOME_BUTTON_ON_GRID_TAB_SWITCHER);
+                        add(StartSurfaceConfiguration.NEW_SURFACE_FROM_HOME_BUTTON);
+                        add(StartSurfaceConfiguration.OMNIBOX_FOCUSED_ON_NEW_TAB);
+                        add(StartSurfaceConfiguration.SHOW_TABS_IN_MRU_ORDER);
+                        add(StartSurfaceConfiguration.START_SURFACE_EXCLUDE_MV_TILES);
+                        add(StartSurfaceConfiguration.START_SURFACE_HIDE_INCOGNITO_SWITCH_NO_TAB);
+                        add(StartSurfaceConfiguration.START_SURFACE_LAST_ACTIVE_TAB_ONLY);
+                        add(StartSurfaceConfiguration.START_SURFACE_OPEN_NTP_INSTEAD_OF_START);
+                        add(StartSurfaceConfiguration.SHOW_NTP_TILES_ON_OMNIBOX);
+                        add(StartSurfaceConfiguration.START_SURFACE_VARIATION);
+                        add(StartSurfaceConfiguration.SUPPORT_ACCESSIBILITY);
+                        add(StartupPaintPreviewHelper.ACCESSIBILITY_SUPPORT_PARAM);
+                        add(CommerceSubscriptionsServiceConfig.STALE_TAB_LOWER_BOUND_SECONDS);
+                        add(CommerceSubscriptionsServiceConfig.SUBSCRIPTIONS_SERVICE_BASE_URL);
+                        add(PriceTrackingUtilities.ENABLE_PRICE_NOTIFICATION);
+                        add(PriceTrackingUtilities.ENABLE_PRICE_TRACKING);
+                        add(TabContentManager.ALLOW_TO_REFETCH_TAB_THUMBNAIL_VARIATION);
+                        add(TabUiFeatureUtilities.ENABLE_LAUNCH_BUG_FIX);
+                        add(TabUiFeatureUtilities.ENABLE_LAUNCH_POLISH);
+                        add(TabUiFeatureUtilities.ENABLE_SEARCH_CHIP);
+                        add(TabUiFeatureUtilities.ENABLE_SEARCH_CHIP_ADAPTIVE);
+                        add(TabUiFeatureUtilities.ENABLE_TAB_GROUP_AUTO_CREATION);
+                        add(TabUiFeatureUtilities.ZOOMING_MIN_MEMORY);
+                        add(TabUiFeatureUtilities.ZOOMING_MIN_SDK);
+                        add(TabUiFeatureUtilities.SKIP_SLOW_ZOOMING);
+                        add(TabUiFeatureUtilities.TAB_GRID_LAYOUT_ANDROID_NEW_TAB_TILE);
+                        add(TabUiFeatureUtilities.THUMBNAIL_ASPECT_RATIO);
+                    }
+                };
         tryToCatchMissingParameters(fieldTrialsToCache);
         CachedFeatureFlags.cacheFieldTrialParameters(fieldTrialsToCache);
 
+        CachedFeatureFlags.onEndCheckpoint();
         mIsFinishedCachingNativeFlags = true;
     }
 
@@ -183,9 +190,14 @@
         CachedFeatureFlags.cacheMinimalBrowserFlagsTimeFromNativeTime();
 
         // TODO(crbug.com/995355): Move other related flags from cacheNativeFlags() to here.
-        CachedFeatureFlags.cacheNativeFlags(Arrays.asList(ChromeFeatureList.EXPERIMENTS_FOR_AGSA,
-                ChromeFeatureList.SERVICE_MANAGER_FOR_DOWNLOAD,
-                ChromeFeatureList.SERVICE_MANAGER_FOR_BACKGROUND_PREFETCH));
+        List<String> featuresToCache = new ArrayList<String>() {
+            {
+                add(ChromeFeatureList.EXPERIMENTS_FOR_AGSA);
+                add(ChromeFeatureList.SERVICE_MANAGER_FOR_DOWNLOAD);
+                add(ChromeFeatureList.SERVICE_MANAGER_FOR_BACKGROUND_PREFETCH);
+            }
+        };
+        CachedFeatureFlags.cacheNativeFlags(featuresToCache);
 
         CachedFeatureFlags.cacheFieldTrialParameters(MINIMAL_BROWSER_FIELD_TRIALS);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
index f21bbe8..f00d0a7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
@@ -302,11 +302,27 @@
     @SuppressLint("MissingSuperCall")  // Called in onCreateInternal.
     protected final void onCreate(Bundle savedInstanceState) {
         TraceEvent.begin("AsyncInitializationActivity.onCreate()");
-        onCreateInternal(savedInstanceState);
+        onPreCreate();
+        boolean willCreate = onCreateInternal(savedInstanceState);
+        if (!willCreate) {
+            onAbortCreate();
+        }
         TraceEvent.end("AsyncInitializationActivity.onCreate()");
     }
 
     /**
+     * Override to perform operations in the first opportunity after the framework calls
+     * {@link #onCreate}. Note the activity may still be aborted by {@link #onCreateInternal}.
+     */
+    protected void onPreCreate() {}
+
+    /**
+     * Override to perform operations after the activity's creation is aborted by {@link
+     * #onCreateInternal}.
+     */
+    protected void onAbortCreate() {}
+
+    /**
      * Called from onCreate() to give derived classes a chance to dispatch the intent using
      * {@link LaunchIntentDispatcher}. If the method returns anything other than Action.CONTINUE,
      * the activity is aborted. Default implementation returns Action.CONTINUE.
@@ -318,7 +334,10 @@
         return LaunchIntentDispatcher.Action.CONTINUE;
     }
 
-    private final void onCreateInternal(Bundle savedInstanceState) {
+    /**
+     * @return true if will proceed with Activity creation, false if will abort.
+     */
+    private final boolean onCreateInternal(Bundle savedInstanceState) {
         initializeStartupMetrics();
         setIntent(IntentHandler.rewriteFromHistoryIntent(getIntent()));
 
@@ -326,20 +345,20 @@
         int dispatchAction = maybeDispatchLaunchIntent(getIntent(), savedInstanceState);
         if (dispatchAction != LaunchIntentDispatcher.Action.CONTINUE) {
             abortLaunch(dispatchAction);
-            return;
+            return false;
         }
 
         Intent intent = getIntent();
         if (!isStartedUpCorrectly(intent)) {
             abortLaunch(LaunchIntentDispatcher.Action.FINISH_ACTIVITY_REMOVE_TASK);
-            return;
+            return false;
         }
 
         if (requiresFirstRunToBeCompleted(intent)
                 && FirstRunFlowSequencer.launch(this, intent, false /* requiresBroadcast */,
                         shouldPreferLightweightFre(intent))) {
             abortLaunch(LaunchIntentDispatcher.Action.FINISH_ACTIVITY);
-            return;
+            return false;
         }
 
         // Some Samsung devices load fonts from disk, crbug.com/691706.
@@ -354,6 +373,7 @@
 
         mStartupDelayed = shouldDelayBrowserStartup();
         ChromeBrowserInitializer.getInstance().handlePreNativeStartupAndLoadLibraries(this);
+        return true;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/LaunchMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/LaunchMetrics.java
index cd73396..15bde10 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/LaunchMetrics.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/LaunchMetrics.java
@@ -9,7 +9,7 @@
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.browserservices.intents.WebDisplayMode;
 import org.chromium.chrome.browser.browserservices.intents.WebappInfo;
-import org.chromium.chrome.browser.webapps.WebApkUkmRecorder;
+import org.chromium.chrome.browser.browserservices.metrics.WebApkUkmRecorder;
 import org.chromium.chrome.browser.webapps.WebappDataStorage;
 import org.chromium.chrome.browser.webapps.WebappRegistry;
 import org.chromium.components.webapps.ShortcutSource;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkUninstallUmaTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkUninstallUmaTracker.java
index 5b6f8c46..d897169 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkUninstallUmaTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkUninstallUmaTracker.java
@@ -6,9 +6,9 @@
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.browserservices.intents.WebappIntentUtils;
+import org.chromium.chrome.browser.browserservices.metrics.WebApkUkmRecorder;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.browser.webapps.WebApkUkmRecorder;
 import org.chromium.chrome.browser.webapps.WebappDataStorage;
 import org.chromium.chrome.browser.webapps.WebappRegistry;
 import org.chromium.components.webapps.WebApkDistributor;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoHistoryController.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoHistoryController.java
index fd6bfc7..9368aa9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoHistoryController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoHistoryController.java
@@ -96,14 +96,14 @@
     // HistoryContentManager.Observer
     @Override
     public void onItemClicked(HistoryItem item) {
-        // TODO(crbug.com/1173154): Add metrics for item clicked.
+        mMainController.recordAction(PageInfoAction.PAGE_INFO_HISTORY_ENTRY_CLICKED);
         return;
     }
 
     // HistoryContentManager.Observer
     @Override
     public void onItemRemoved(HistoryItem item) {
-        // TODO(crbug.com/1173154): Add metrics for item removed.
+        mMainController.recordAction(PageInfoAction.PAGE_INFO_HISTORY_ENTRY_REMOVED);
         return;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
index 7b35ac6..e46bfbc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
@@ -192,7 +192,8 @@
                 IntentHandler::bringTabToFront,
                 /*saveOfflineButtonState=*/(tab) -> false, /*omniboxUma*/(url, transition) -> {},
                 TabWindowManagerSingleton::getInstance, /*bookmarkState=*/(url) -> false,
-                VoiceToolbarButtonController::isToolbarMicEnabled, new DummyJankTracker());
+                VoiceToolbarButtonController::isToolbarMicEnabled, new DummyJankTracker(),
+                /*ExploreIconState*/(pixelSize, callback) ->{});
         // clang-format on
         mLocationBarCoordinator.setUrlBarFocusable(true);
         mLocationBarCoordinator.setShouldShowMicButtonWhenUnfocused(true);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 337493b..b8621a1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -55,6 +55,7 @@
 import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbar;
 import org.chromium.chrome.browser.dom_distiller.DomDistillerTabUtils;
 import org.chromium.chrome.browser.download.DownloadUtils;
+import org.chromium.chrome.browser.explore_sites.ExploreSitesBridge;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.feed.shared.FeedFeatures;
 import org.chromium.chrome.browser.findinpage.FindToolbarManager;
@@ -84,6 +85,7 @@
 import org.chromium.chrome.browser.omnibox.OverrideUrlLoadingDelegate;
 import org.chromium.chrome.browser.omnibox.SearchEngineLogoUtils;
 import org.chromium.chrome.browser.omnibox.UrlFocusChangeListener;
+import org.chromium.chrome.browser.omnibox.suggestions.mostvisited.ExploreIconProvider;
 import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
 import org.chromium.chrome.browser.page_info.ChromePageInfo;
 import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
@@ -542,6 +544,13 @@
                             postDataType, postData, incognito, startSurfaceParentTabSupplier.get());
             ChromePageInfo toolbarPageInfo =
                     new ChromePageInfo(modalDialogManagerSupplier, null, OpenedFromSource.TOOLBAR);
+            ExploreIconProvider exploreIconProvider = (pixelSize, callback) -> {
+                if (profileSupplier.get() != null) {
+                    ExploreSitesBridge.getSummaryImage(profileSupplier.get(), pixelSize, callback);
+                } else {
+                    callback.onResult(null);
+                }
+            };
             // clang-format off
             LocationBarCoordinator locationBarCoordinator = new LocationBarCoordinator(
                     mActivity.findViewById(R.id.location_bar), toolbarLayout, profileSupplier,
@@ -557,7 +566,8 @@
                     TabWindowManagerSingleton::getInstance,
                     (url) -> mBookmarkBridgeSupplier.hasValue()
                             && mBookmarkBridgeSupplier.get().isBookmarked(url),
-                    VoiceToolbarButtonController::isToolbarMicEnabled, jankTracker);
+                    VoiceToolbarButtonController::isToolbarMicEnabled, jankTracker,
+                    exploreIconProvider);
             // clang-format on
             toolbarLayout.setLocationBarCoordinator(locationBarCoordinator);
             mLocationBar = locationBarCoordinator;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivityLifecycleUmaTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivityLifecycleUmaTracker.java
index 432191f..733c6ae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivityLifecycleUmaTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivityLifecycleUmaTracker.java
@@ -21,6 +21,7 @@
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.browserservices.intents.WebApkExtras;
 import org.chromium.chrome.browser.browserservices.intents.WebappIntentUtils;
+import org.chromium.chrome.browser.browserservices.metrics.WebApkUkmRecorder;
 import org.chromium.chrome.browser.browserservices.metrics.WebApkUmaRecorder;
 import org.chromium.chrome.browser.browserservices.ui.splashscreen.SplashController;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialog.java
index c0d425d2..671c513 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialog.java
@@ -124,7 +124,8 @@
                 break;
             case ModalDialogProperties.ButtonType.NEGATIVE:
                 WebApkUpdateReportAbuseDialog reportAbuseDialog = new WebApkUpdateReportAbuseDialog(
-                        mModalDialogManager, mPackageName, mOldAppShortName, this::onUninstall);
+                        mModalDialogManager, mPackageName, mOldAppShortName,
+                        /* showAbuseCheckbox= */ false, this::onUninstall);
                 reportAbuseDialog.show();
                 break;
             default:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateReportAbuseDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateReportAbuseDialog.java
index 6b6524b..efbb85b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateReportAbuseDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateReportAbuseDialog.java
@@ -45,6 +45,9 @@
     // The package name for the app the user is uninstalling.
     private String mAppPackageName;
 
+    // Whether to show the checkbox for reporting abuse.
+    private boolean mShowAbuseCheckbox;
+
     // When checked, the app will not just be uninstalled, but also reported for abuse.
     private CheckBox mReportAbuseCheckBox;
 
@@ -52,10 +55,11 @@
     private Callback mOnUninstallCallback;
 
     public WebApkUpdateReportAbuseDialog(ModalDialogManager manager, String appPackageName,
-            String appShortName, Callback callback) {
+            String appShortName, boolean showAbuseCheckbox, Callback callback) {
         mModalDialogManager = manager;
         mAppPackageName = appPackageName;
         mAppShortName = appShortName;
+        mShowAbuseCheckbox = showAbuseCheckbox;
         mOnUninstallCallback = callback;
     }
 
@@ -66,23 +70,24 @@
         Context context = ContextUtils.getApplicationContext();
         Resources resources = context.getResources();
 
-        View dialogCustomView = LayoutInflaterUtils.inflate(
-                context, R.layout.webapk_update_report_abuse_custom_view, null);
-        mReportAbuseCheckBox = dialogCustomView.findViewById(R.id.report_abuse);
-
         String title =
                 resources.getString(R.string.webapk_report_abuse_dialog_title, mAppShortName);
-        PropertyModel dialogModel =
+        PropertyModel.Builder builder =
                 new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
                         .with(ModalDialogProperties.CONTROLLER, this)
                         .with(ModalDialogProperties.TITLE, title)
-                        .with(ModalDialogProperties.CUSTOM_VIEW, dialogCustomView)
                         .with(ModalDialogProperties.PRIMARY_BUTTON_FILLED, true)
                         .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources,
                                 R.string.webapk_report_abuse_confirm)
                         .with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, resources,
-                                R.string.webapk_report_abuse_cancel)
-                        .build();
+                                R.string.webapk_report_abuse_cancel);
+        if (mShowAbuseCheckbox) {
+            View dialogCustomView = LayoutInflaterUtils.inflate(
+                    context, R.layout.webapk_update_report_abuse_custom_view, null);
+            mReportAbuseCheckBox = dialogCustomView.findViewById(R.id.report_abuse);
+            builder = builder.with(ModalDialogProperties.CUSTOM_VIEW, dialogCustomView);
+        }
+        PropertyModel dialogModel = builder.build();
 
         mModalDialogManager.showDialog(dialogModel, ModalDialogManager.ModalDialogType.APP);
     }
@@ -108,7 +113,7 @@
         if (dismissalCause == DialogDismissalCause.POSITIVE_BUTTON_CLICKED) {
             mOnUninstallCallback.onUninstall();
 
-            if (mReportAbuseCheckBox.isChecked()) {
+            if (mShowAbuseCheckbox && mReportAbuseCheckBox.isChecked()) {
                 // TODO(finnur): Implement sending info to the SafeBrowsing team.
                 Log.i(TAG, "Send report to SafeBrowsing");
             }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentBasicTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentBasicTest.java
index 88c4345..0e6e9ef4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentBasicTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentBasicTest.java
@@ -10,8 +10,12 @@
 import static org.mockito.Mockito.when;
 import static org.mockito.MockitoAnnotations.initMocks;
 
+import android.view.View;
+
+import androidx.annotation.Nullable;
 import androidx.preference.CheckBoxPreference;
 import androidx.preference.PreferenceScreen;
+import androidx.test.filters.LargeTest;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
@@ -21,19 +25,31 @@
 import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 
 import org.chromium.base.CollectionUtil;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.R;
 import org.chromium.chrome.browser.browsing_data.ClearBrowsingDataFragment.DialogOption;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.browser.settings.SettingsActivityTestRule;
 import org.chromium.chrome.browser.sync.SyncService;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.ChromeRenderTestRule;
+import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.chrome.test.util.browser.signin.AccountManagerTestRule;
+import org.chromium.components.search_engines.TemplateUrl;
+import org.chromium.components.search_engines.TemplateUrlService;
 import org.chromium.components.sync.ModelType;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
+import java.io.IOException;
 import java.util.HashSet;
 
 /**
@@ -57,6 +73,10 @@
     @Rule
     public final AccountManagerTestRule mAccountManagerTestRule = new AccountManagerTestRule();
 
+    @Rule
+    public ChromeRenderTestRule mRenderTestRule =
+            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+
     private static final String GOOGLE_ACCOUNT = "Google Account";
     private static final String OTHER_ACTIVITY = "other forms of browsing history";
     private static final String SYNCED_DEVICES = "synced devices";
@@ -64,6 +84,13 @@
     @Mock
     private SyncService mMockSyncService;
 
+    @Mock
+    public TemplateUrlService mMockTemplateUrlService;
+    @Mock
+    public TemplateUrl mMockSearchEngine;
+
+    private @Nullable TemplateUrlService mActualTemplateUrlService;
+
     @Before
     public void setUp() throws InterruptedException {
         initMocks(this);
@@ -75,6 +102,10 @@
     @After
     public void tearDown() {
         TestThreadUtils.runOnUiThreadBlocking(() -> SyncService.resetForTests());
+        if (mActualTemplateUrlService != null) {
+            // Reset the actual service if the mock is used.
+            TemplateUrlServiceFactory.setInstanceForTesting(mActualTemplateUrlService);
+        }
     }
 
     private void setSyncable(boolean syncable) {
@@ -87,11 +118,31 @@
         });
     }
 
+    private void configureMockSearchEngine() {
+        // Cache the actual Url Service, so the test can put it back after tests.
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { mActualTemplateUrlService = TemplateUrlServiceFactory.get(); });
+
+        TemplateUrlServiceFactory.setInstanceForTesting(mMockTemplateUrlService);
+        Mockito.doReturn(mMockSearchEngine)
+                .when(mMockTemplateUrlService)
+                .getDefaultSearchEngineTemplateUrl();
+    }
+
     private String getCheckboxSummary(PreferenceScreen screen, String preference) {
         CheckBoxPreference checkbox = (CheckBoxPreference) screen.findPreference(preference);
         return new StringBuilder(checkbox.getSummary()).toString();
     }
 
+    private void waitForOptionsMenu() {
+        // TODO(crbug.com/1227122): find a better solution that doesn't involve waiting for a single
+        // button to be displayed but instead waits for all the views to finish getting drawn.
+        CriteriaHelper.pollUiThread(() -> {
+            return mSettingsActivityTestRule.getActivity().findViewById(R.id.menu_id_general_help)
+                    != null;
+        });
+    }
+
     /**
      * Tests that for users who are not signed in, only the general information is shown.
      */
@@ -173,4 +224,148 @@
             assertThat(historySummary, containsString(SYNCED_DEVICES));
         });
     }
+
+    @Test
+    @LargeTest
+    @Feature({"RenderTest"})
+    @DisableFeatures(ChromeFeatureList.SEARCH_HISTORY_LINK)
+    public void testRenderNotSignedIn() throws IOException {
+        mSettingsActivityTestRule.startSettingsActivity();
+        waitForOptionsMenu();
+        View view = mSettingsActivityTestRule.getActivity()
+                            .findViewById(android.R.id.content)
+                            .getRootView();
+        mRenderTestRule.render(view, "clear_browsing_data_basic_signed_out");
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"RenderTest"})
+    @DisableFeatures(ChromeFeatureList.SEARCH_HISTORY_LINK)
+    public void testRenderSignedInNotSyncing() throws IOException {
+        mAccountManagerTestRule.addTestAccountThenSigninAndEnableSync();
+        // Simulate that Sync was stopped but the primary account remained.
+        setSyncable(false);
+        mSettingsActivityTestRule.startSettingsActivity();
+        waitForOptionsMenu();
+        View view = mSettingsActivityTestRule.getActivity()
+                            .findViewById(android.R.id.content)
+                            .getRootView();
+        mRenderTestRule.render(view, "clear_browsing_data_basic_signed_in_no_sync");
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"RenderTest"})
+    @DisableFeatures(ChromeFeatureList.SEARCH_HISTORY_LINK)
+    public void testRenderSignedInAndSyncing() throws IOException {
+        mAccountManagerTestRule.addTestAccountThenSigninAndEnableSync();
+        setSyncable(true);
+        mSettingsActivityTestRule.startSettingsActivity();
+        waitForOptionsMenu();
+        View view = mSettingsActivityTestRule.getActivity()
+                            .findViewById(android.R.id.content)
+                            .getRootView();
+        mRenderTestRule.render(view, "clear_browsing_data_basic_signed_in_sync");
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"RenderTest"})
+    @EnableFeatures(ChromeFeatureList.SEARCH_HISTORY_LINK)
+    public void testRenderSearchHistoryLinkSignedOutGoogleDSE() throws IOException {
+        mSettingsActivityTestRule.startSettingsActivity();
+        waitForOptionsMenu();
+        View view = mSettingsActivityTestRule.getActivity()
+                            .findViewById(android.R.id.content)
+                            .getRootView();
+        mRenderTestRule.render(view, "clear_browsing_data_basic_shl_google_signed_out");
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"RenderTest"})
+    @EnableFeatures(ChromeFeatureList.SEARCH_HISTORY_LINK)
+    public void testRenderSearchHistoryLinkSignedInGoogleDSE() throws IOException {
+        mAccountManagerTestRule.addTestAccountThenSigninAndEnableSync();
+        setSyncable(false);
+        mSettingsActivityTestRule.startSettingsActivity();
+        waitForOptionsMenu();
+        View view = mSettingsActivityTestRule.getActivity()
+                            .findViewById(android.R.id.content)
+                            .getRootView();
+        mRenderTestRule.render(view, "clear_browsing_data_basic_shl_google_signed_in");
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"RenderTest"})
+    @EnableFeatures(ChromeFeatureList.SEARCH_HISTORY_LINK)
+    public void testRenderSearchHistoryLinkSignedInKnownNonGoogleDSE() throws IOException {
+        mAccountManagerTestRule.addTestAccountThenSigninAndEnableSync();
+        setSyncable(false);
+        configureMockSearchEngine();
+        Mockito.doReturn(false).when(mMockTemplateUrlService).isDefaultSearchEngineGoogle();
+        Mockito.doReturn(true).when(mMockSearchEngine).getIsPrepopulated();
+
+        mSettingsActivityTestRule.startSettingsActivity();
+        waitForOptionsMenu();
+        View view = mSettingsActivityTestRule.getActivity()
+                            .findViewById(android.R.id.content)
+                            .getRootView();
+        mRenderTestRule.render(view, "clear_browsing_data_basic_shl_known_signed_in");
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"RenderTest"})
+    @EnableFeatures(ChromeFeatureList.SEARCH_HISTORY_LINK)
+    public void testRenderSearchHistoryLinkSignedInUnknownNonGoogleDSE() throws IOException {
+        mAccountManagerTestRule.addTestAccountThenSigninAndEnableSync();
+        setSyncable(false);
+        configureMockSearchEngine();
+        Mockito.doReturn(false).when(mMockTemplateUrlService).isDefaultSearchEngineGoogle();
+        Mockito.doReturn(false).when(mMockSearchEngine).getIsPrepopulated();
+
+        mSettingsActivityTestRule.startSettingsActivity();
+        waitForOptionsMenu();
+        View view = mSettingsActivityTestRule.getActivity()
+                            .findViewById(android.R.id.content)
+                            .getRootView();
+        mRenderTestRule.render(view, "clear_browsing_data_basic_shl_unknown_signed_in");
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"RenderTest"})
+    @EnableFeatures(ChromeFeatureList.SEARCH_HISTORY_LINK)
+    public void testRenderSearchHistoryLinkSignedOutKnownNonGoogleDSE() throws IOException {
+        configureMockSearchEngine();
+        Mockito.doReturn(false).when(mMockTemplateUrlService).isDefaultSearchEngineGoogle();
+        Mockito.doReturn(true).when(mMockSearchEngine).getIsPrepopulated();
+
+        mSettingsActivityTestRule.startSettingsActivity();
+        waitForOptionsMenu();
+        View view = mSettingsActivityTestRule.getActivity()
+                            .findViewById(android.R.id.content)
+                            .getRootView();
+        mRenderTestRule.render(view, "clear_browsing_data_basic_shl_known_signed_out");
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"RenderTest"})
+    @EnableFeatures(ChromeFeatureList.SEARCH_HISTORY_LINK)
+    public void testRenderSearchHistoryLinkSignedOutUnknownNonGoogleDSE() throws IOException {
+        configureMockSearchEngine();
+        Mockito.doReturn(false).when(mMockTemplateUrlService).isDefaultSearchEngineGoogle();
+        Mockito.doReturn(false).when(mMockSearchEngine).getIsPrepopulated();
+
+        mSettingsActivityTestRule.startSettingsActivity();
+        waitForOptionsMenu();
+        View view = mSettingsActivityTestRule.getActivity()
+                            .findViewById(android.R.id.content)
+                            .getRootView();
+        mRenderTestRule.render(view, "clear_browsing_data_basic_shl_unknown_signed_out");
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
index 451f8db..9304efa 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
@@ -220,7 +220,8 @@
         mMediator = new AutocompleteMediator(ContextUtils.getApplicationContext(),
                 mAutocompleteDelegate, mTextStateProvider, mListModel,
                 mHandler, () -> mModalDialogManager, null, null,
-                mLocationBarDataProvider, tab -> {}, null, url -> false, new DummyJankTracker());
+                mLocationBarDataProvider, tab -> {}, null, url -> false, new DummyJankTracker(),
+                (pixelSize, callback) -> {});
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mMediator.setAutocompleteProfile(mProfile);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilderUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilderUnitTest.java
index 37938a3..fbebfd8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilderUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilderUnitTest.java
@@ -81,7 +81,8 @@
                 .thenAnswer((mock) -> new PropertyModel(SuggestionCommonProperties.ALL_KEYS));
         when(mMockHeaderProcessor.getViewTypeId()).thenReturn(OmniboxSuggestionUiType.HEADER);
 
-        mBuilder = new DropdownItemViewInfoListBuilder(() -> null, (url) -> false);
+        mBuilder = new DropdownItemViewInfoListBuilder(
+                () -> null, (url) -> false, (pixelSize, callback) -> {});
         mBuilder.registerSuggestionProcessor(mMockSuggestionProcessor);
         mBuilder.setHeaderProcessorForTest(mMockHeaderProcessor);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java
new file mode 100644
index 0000000..cfda4bf
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java
@@ -0,0 +1,121 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.omnibox.suggestions.mostvisited;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.util.DisplayMetrics;
+
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.util.Batch;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
+import org.chromium.chrome.browser.omnibox.suggestions.carousel.BaseCarouselSuggestionViewProperties;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.components.browser_ui.util.GlobalDiscardableReferencePool;
+import org.chromium.components.embedder_support.util.UrlConstants;
+import org.chromium.components.omnibox.AutocompleteMatch;
+import org.chromium.components.omnibox.AutocompleteMatch.NavsuggestTile;
+import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.url.GURL;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link MostVisitedTilesProcessor}.
+ */
+@RunWith(BaseJUnit4ClassRunner.class)
+@Batch(Batch.UNIT_TESTS)
+public final class MostVisitedTilesProcessorUnitTest {
+    private static final GURL EXPLORE_SITES_URL = new GURL(UrlConstants.EXPLORE_URL);
+    private int mCallbackCount;
+
+    @Rule
+    public TestRule mFeaturesProcessor = new Features.JUnitProcessor();
+
+    @Mock
+    Context mContext;
+    @Mock
+    Resources mResources;
+    @Mock
+    SuggestionHost mSuggestionHost;
+    @Mock
+    PropertyModel mPropertyModel;
+    @Mock
+    AutocompleteMatch mSuggestion;
+    @Mock
+    DisplayMetrics mDisplayMetrics;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        NativeLibraryTestUtils.loadNativeLibraryNoBrowserProcess();
+
+        ArrayList<PropertyKey> allProperties =
+                new ArrayList<>(Arrays.asList(BaseCarouselSuggestionViewProperties.ALL_KEYS));
+        mPropertyModel = new PropertyModel(allProperties);
+    }
+
+    @Test
+    @MediumTest
+    public void doNotFetchExploreSiteIconWithCachedReady() {
+        List<NavsuggestTile> tiles = new ArrayList<>();
+        tiles.add(new NavsuggestTile("explore_sites", EXPLORE_SITES_URL));
+        Mockito.doReturn(tiles).when(mSuggestion).getNavsuggestTiles();
+
+        Bitmap exploreSitesIcon = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
+        mCallbackCount = 0;
+        ExploreIconProvider exploreIconProvider = (pixelSize, callback) -> {
+            callback.onResult(exploreSitesIcon);
+            mCallbackCount++;
+        };
+
+        Mockito.doReturn(mResources).when(mContext).getResources();
+        Mockito.doReturn(84)
+                .when(mResources)
+                .getDimensionPixelSize(
+                        org.chromium.chrome.browser.omnibox.R.dimen.tile_view_icon_size);
+        Mockito.doReturn(24)
+                .when(mResources)
+                .getDimensionPixelSize(org.chromium.chrome.browser.omnibox.R.dimen
+                                               .omnibox_suggestion_favicon_size);
+        Mockito.doReturn(mDisplayMetrics).when(mResources).getDisplayMetrics();
+
+        MostVisitedTilesProcessor processor = new MostVisitedTilesProcessor(mContext,
+                mSuggestionHost,
+                () -> null, exploreIconProvider, GlobalDiscardableReferencePool.getReferencePool());
+
+        Assert.assertNull(processor.getExploreSitesIconForTesting());
+        processor.populateModel(mSuggestion, mPropertyModel, 0);
+        // Verifies that mExploreIconState#getSummaryImage() is called when there isn't any cached
+        // explore sites icon.
+        Assert.assertNotNull(processor.getExploreSitesIconForTesting());
+        Assert.assertEquals(1, mCallbackCount);
+
+        RoundedBitmapDrawable expectedExploreSitesIcon = processor.getExploreSitesIconForTesting();
+        processor.populateModel(mSuggestion, mPropertyModel, 0);
+        // Verifies that mExploreIconState#getSummaryImage() is no longer called when there is a
+        // cached explore sites icon.
+        Assert.assertEquals(1, mCallbackCount);
+        Assert.assertEquals(expectedExploreSitesIcon, processor.getExploreSitesIconForTesting());
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandlerTest.java
index 8fb5aeb..14dec474 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandlerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandlerTest.java
@@ -10,7 +10,6 @@
 import static org.mockito.ArgumentMatchers.notNull;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyLong;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -425,7 +424,8 @@
             // clang-format off
             super(parent, delegate, dropdownEmbedder, urlBarEditingTextProvider,
                     () -> mModalDialogManager, null, null, mDataProvider,
-                    mProfileSupplier, (tab) -> {}, null, (url) -> false, new DummyJankTracker());
+                    mProfileSupplier, (tab) -> {}, null, (url) -> false, new DummyJankTracker(),
+                    (pixelSize, callback) -> {});
             // clang-format on
         }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/LocationBarModelTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/LocationBarModelTest.java
index 6aa5f134..8ea584a7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/LocationBarModelTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/LocationBarModelTest.java
@@ -105,6 +105,12 @@
             model.mDisplayUrl = "foo.com";
             model.mFullUrl = "https://foo.com";
             assertDisplayAndEditText(model, "foo.com", "https://foo.com");
+
+            // https://crbug.com/1214481
+            model.mUrl = "";
+            model.mDisplayUrl = "about:blank";
+            model.mFullUrl = "about:blank";
+            assertDisplayAndEditText(model, "about:blank", "about:blank");
         });
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialogTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialogTest.java
index a07e8c9..8fec3ee 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialogTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialogTest.java
@@ -185,8 +185,9 @@
             boolean clickAccept, String shortAppName, String expectedTitle) throws Exception {
         int callCount = mOnActionCallback.getCallCount();
 
-        WebApkUpdateReportAbuseDialog dialog = new WebApkUpdateReportAbuseDialog(
-                mDialogManager, /* packageName= */ "", shortAppName, this::onAbuseDialogResult);
+        WebApkUpdateReportAbuseDialog dialog =
+                new WebApkUpdateReportAbuseDialog(mDialogManager, /* packageName= */ "",
+                        shortAppName, /* showAbuseCheckbox= */ true, this::onAbuseDialogResult);
         dialog.show();
 
         Assert.assertEquals(expectedTitle, getDialogTitle());
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index dd05a92..4364777a 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-93.0.4568.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-93.0.4570.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index 77da078..b19f900 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -125,6 +125,7 @@
 #define IDC_SHARING_HUB                 35028
 #define IDC_SHARING_HUB_MENU            35029
 #define IDC_VIRTUAL_CARD_MANUAL_FALLBACK 35030
+#define IDC_SHARING_HUB_SCREENSHOT      35031
 
 // Page-manipulation commands that target a specified tab, which may not be the
 // active one.
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 7b8b881d..0a1ddee 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -2498,6 +2498,29 @@
         Open as tabbed window
       </message>
 
+      <!-- WebApp name/icon update dialog -->
+      <message name="IDS_WEBAPP_UPDATE_DIALOG_TITLE_NAME" desc="The title at the top of the dialog when only the name changed.">
+        Review name updates
+      </message>
+      <message name="IDS_WEBAPP_UPDATE_DIALOG_TITLE_ICON" desc="The title at the top of the dialog when only the icon changed.">
+        Review icon updates
+      </message>
+      <message name="IDS_WEBAPP_UPDATE_DIALOG_TITLE_NAME_AND_ICON" desc="The title at the top of the dialog when the name and icon changed.">
+        Review name &amp; icon updates
+      </message>
+      <message name="IDS_WEBAPP_UPDATE_EXPLANATION" desc="The text at the top of the dialog, explaining what to do when an app changes its identity.">
+        If this web app is trying to trick you into thinking it is a different app, uninstall it.
+      </message>
+      <message name="IDS_WEBAPP_UPDATE_NEGATIVE_BUTTON" desc="The text for the negative button (aka. cancel button), allowing the user to uninstall the app and report it to the abuse team.">
+        Uninstall app
+      </message>
+      <message name="IDS_UPDATE_WEBAPP_CURRENT_ICON" desc="The accessibility string explaining which icon is the current icon.">
+        Current icon
+      </message>
+      <message name="IDS_UPDATE_WEBAPP_UPDATED_ICON" desc="The accessibility string explaining which icon is the new icon.">
+        New icon
+      </message>
+
       <!-- Finish policy app installation dialog -->
       <message name="IDS_FINISH_POLICY_WEB_APP_INSTALLATION" desc="Main text for the Finish Policy Web App Installation dialog. This is a dialog message that appears on the first launch of an app after it was remotely installed by an enterprise administrator. The app has some cosmetic factors (like the app icon) that can only be fixed if the user launches and then restarts the app. After the initial launch, this dialog encourages users to restart the app to complete the installation.">
           This app was added by your organization. Restart the app to finish installing it.
@@ -7737,6 +7760,9 @@
       <message name="IDS_SHARING_HUB_TOOLTIP" desc="Tooltip for the Sharing Hub feature omnibox icon.">
         Share this page
       </message>
+      <message name="IDS_SHARING_HUB_SCREENSHOT_LABEL" desc="The text label for the screenshot button in the Sharing Hub dialog and app menu.">
+        Screenshot
+      </message>
       <message name="IDS_SHARING_HUB_COPY_LINK_LABEL" desc="The text label for the copy link button in the Sharing Hub dialog and app menu.">
         Copy link
       </message>
@@ -11364,6 +11390,11 @@
       Google Drive
     </message>
 
+    <!-- Enterprise file system connector sign in dialog -->
+    <message name="IDS_FILE_SYSTEM_CONNECTOR_SIGNIN_DIALOG_TITLE" desc="Title shown in dialog when prompting user to sign in to link a web drive account.">
+      Link <ph name="WEB_DRIVE">$1<ex>Google Drive</ex></ph> account for Downloads
+    </message>
+
     <!-- App uninstall prompt title -->
     <message name="IDS_PROMPT_APP_UNINSTALL_TITLE" desc="Title text for uninstalling an app.">
       Uninstall "<ph name="APP_NAME">$1<ex>Gmail Checker</ex></ph>"?
diff --git a/chrome/app/generated_resources_grd/IDS_FILE_SYSTEM_CONNECTOR_BOX.png.sha1 b/chrome/app/generated_resources_grd/IDS_FILE_SYSTEM_CONNECTOR_BOX.png.sha1
index 25c1bb6..4dd4537 100644
--- a/chrome/app/generated_resources_grd/IDS_FILE_SYSTEM_CONNECTOR_BOX.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_FILE_SYSTEM_CONNECTOR_BOX.png.sha1
@@ -1 +1 @@
-2ebb50b84eb0e8e50ed15c782676293f6cc3f2f4
\ No newline at end of file
+0f6fbdc0a14b66effd1ac8fc5a4b3045b7eadf6d
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_FILE_SYSTEM_CONNECTOR_SIGNIN_DIALOG_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_FILE_SYSTEM_CONNECTOR_SIGNIN_DIALOG_TITLE.png.sha1
new file mode 100644
index 0000000..4dd4537
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_FILE_SYSTEM_CONNECTOR_SIGNIN_DIALOG_TITLE.png.sha1
@@ -0,0 +1 @@
+0f6fbdc0a14b66effd1ac8fc5a4b3045b7eadf6d
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_SHARING_HUB_SCREENSHOT_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_SHARING_HUB_SCREENSHOT_LABEL.png.sha1
new file mode 100644
index 0000000..dcbd9d6f
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_SHARING_HUB_SCREENSHOT_LABEL.png.sha1
@@ -0,0 +1 @@
+e9bf5ae8bf00fee232fa3b2d5d5c1361ff0c1d11
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_UPDATE_WEBAPP_CURRENT_ICON.png.sha1 b/chrome/app/generated_resources_grd/IDS_UPDATE_WEBAPP_CURRENT_ICON.png.sha1
new file mode 100644
index 0000000..0860acb
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_UPDATE_WEBAPP_CURRENT_ICON.png.sha1
@@ -0,0 +1 @@
+70a25cf918959e716c27dbc0652d1ad6b400f6d6
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_UPDATE_WEBAPP_UPDATED_ICON.png.sha1 b/chrome/app/generated_resources_grd/IDS_UPDATE_WEBAPP_UPDATED_ICON.png.sha1
new file mode 100644
index 0000000..0860acb
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_UPDATE_WEBAPP_UPDATED_ICON.png.sha1
@@ -0,0 +1 @@
+70a25cf918959e716c27dbc0652d1ad6b400f6d6
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_WEBAPP_UPDATE_DIALOG_TITLE_ICON.png.sha1 b/chrome/app/generated_resources_grd/IDS_WEBAPP_UPDATE_DIALOG_TITLE_ICON.png.sha1
new file mode 100644
index 0000000..046d794
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_WEBAPP_UPDATE_DIALOG_TITLE_ICON.png.sha1
@@ -0,0 +1 @@
+89752956d6c66d449c7d8eac2d5ac76f63acf9a3
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_WEBAPP_UPDATE_DIALOG_TITLE_NAME.png.sha1 b/chrome/app/generated_resources_grd/IDS_WEBAPP_UPDATE_DIALOG_TITLE_NAME.png.sha1
new file mode 100644
index 0000000..ad7cad5
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_WEBAPP_UPDATE_DIALOG_TITLE_NAME.png.sha1
@@ -0,0 +1 @@
+bc9dfc7931813a2e3cc421a343331e197dfb85f9
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_WEBAPP_UPDATE_DIALOG_TITLE_NAME_AND_ICON.png.sha1 b/chrome/app/generated_resources_grd/IDS_WEBAPP_UPDATE_DIALOG_TITLE_NAME_AND_ICON.png.sha1
new file mode 100644
index 0000000..0860acb
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_WEBAPP_UPDATE_DIALOG_TITLE_NAME_AND_ICON.png.sha1
@@ -0,0 +1 @@
+70a25cf918959e716c27dbc0652d1ad6b400f6d6
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_WEBAPP_UPDATE_EXPLANATION.png.sha1 b/chrome/app/generated_resources_grd/IDS_WEBAPP_UPDATE_EXPLANATION.png.sha1
new file mode 100644
index 0000000..0860acb
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_WEBAPP_UPDATE_EXPLANATION.png.sha1
@@ -0,0 +1 @@
+70a25cf918959e716c27dbc0652d1ad6b400f6d6
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_WEBAPP_UPDATE_NEGATIVE_BUTTON.png.sha1 b/chrome/app/generated_resources_grd/IDS_WEBAPP_UPDATE_NEGATIVE_BUTTON.png.sha1
new file mode 100644
index 0000000..0860acb
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_WEBAPP_UPDATE_NEGATIVE_BUTTON.png.sha1
@@ -0,0 +1 @@
+70a25cf918959e716c27dbc0652d1ad6b400f6d6
\ No newline at end of file
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index 0b23cd5..be2d68c 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -97,6 +97,7 @@
     "scrolling_tabstrip_trailing.icon",
     "security.icon",
     "send_tab_to_self.icon",
+    "sharing_hub_screenshot.icon",
     "side_panel.icon",
     "side_panel_touch.icon",
     "sign_out.icon",
diff --git a/chrome/app/vector_icons/sharing_hub_screenshot.icon b/chrome/app/vector_icons/sharing_hub_screenshot.icon
new file mode 100644
index 0000000..66d4450d
--- /dev/null
+++ b/chrome/app/vector_icons/sharing_hub_screenshot.icon
@@ -0,0 +1,30 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 24,
+
+MOVE_TO, 8.6f, 2,
+H_LINE_TO, 4.4f,
+CUBIC_TO, 3.6f, 2, 3, 2.6f, 3, 3.4f,
+R_V_LINE_TO, 4.2f,
+R_H_LINE_TO, 0,
+
+MOVE_TO, 8.6f, 18.8f,
+H_LINE_TO, 4.4f,
+R_CUBIC_TO, -0.8f, 0, -1.4f, -0.6f, -1.4f, -1.4f,
+R_V_LINE_TO, -4.2f,
+R_H_LINE_TO, 0,
+
+MOVE_TO, 14, 2,
+R_H_LINE_TO, 4.2f,
+R_CUBIC_TO, 0.8f, 0, 1.4f, 0.6f, 1.4f, 1.4f,
+R_V_LINE_TO, 4.2f,
+R_H_LINE_TO, 0,
+
+MOVE_TO, 19.8f, 12.5f,
+R_V_LINE_TO, 9.8f,
+
+MOVE_TO, 14, 18.8f,
+R_H_LINE_TO, 8.5f,
+CLOSE
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 4b15ac4..43932f89d9 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -165,6 +165,8 @@
     "autofill/autocomplete_history_manager_factory.h",
     "autofill/autofill_gstatic_reader.cc",
     "autofill/autofill_gstatic_reader.h",
+    "autofill/autofill_image_fetcher_factory.cc",
+    "autofill/autofill_image_fetcher_factory.h",
     "autofill/autofill_offer_manager_factory.cc",
     "autofill/autofill_offer_manager_factory.h",
     "autofill/autofill_profile_validator_factory.cc",
@@ -1650,12 +1652,14 @@
     "ssl/connection_help_tab_helper.h",
     "ssl/https_defaulted_callbacks.cc",
     "ssl/https_defaulted_callbacks.h",
+    "ssl/https_only_mode_controller_client.cc",
+    "ssl/https_only_mode_controller_client.h",
     "ssl/https_only_mode_navigation_throttle.cc",
     "ssl/https_only_mode_navigation_throttle.h",
     "ssl/https_only_mode_policy_handler.cc",
     "ssl/https_only_mode_policy_handler.h",
-    "ssl/https_only_mode_tab_storage.cc",
-    "ssl/https_only_mode_tab_storage.h",
+    "ssl/https_only_mode_tab_helper.cc",
+    "ssl/https_only_mode_tab_helper.h",
     "ssl/https_only_mode_upgrade_interceptor.cc",
     "ssl/https_only_mode_upgrade_interceptor.h",
     "ssl/https_only_mode_upgrade_url_loader.cc",
@@ -1921,6 +1925,8 @@
     "//chrome/browser/metrics:expired_histograms_array",
     "//chrome/browser/metrics/variations:chrome_ui_string_overrider_factory",
     "//chrome/browser/net:probe_message_proto",
+    "//chrome/browser/new_tab_page/modules/drive:mojo_bindings",
+    "//chrome/browser/new_tab_page/modules/task_module:mojo_bindings",
     "//chrome/browser/notifications",
     "//chrome/browser/notifications/scheduler:factory",
     "//chrome/browser/notifications/scheduler/public",
@@ -1939,8 +1945,6 @@
     "//chrome/browser/safe_browsing:advanced_protection",
     "//chrome/browser/safe_browsing:metrics_collector",
     "//chrome/browser/safe_browsing:verdict_cache_manager_factory",
-    "//chrome/browser/search/drive:mojo_bindings",
-    "//chrome/browser/search/task_module:mojo_bindings",
     "//chrome/browser/sharing:buildflags",
     "//chrome/browser/sharing/proto",
     "//chrome/browser/signin:identity_manager_provider",
@@ -3804,6 +3808,28 @@
       "metrics/usage_scenario/usage_scenario_tracker.h",
       "metrics/usage_scenario/video_capture_event_provider.cc",
       "metrics/usage_scenario/video_capture_event_provider.h",
+      "new_tab_page/modules/drive/drive_handler.cc",
+      "new_tab_page/modules/drive/drive_handler.h",
+      "new_tab_page/modules/drive/drive_service.cc",
+      "new_tab_page/modules/drive/drive_service.h",
+      "new_tab_page/modules/drive/drive_service_factory.cc",
+      "new_tab_page/modules/drive/drive_service_factory.h",
+      "new_tab_page/modules/task_module/task_module_handler.cc",
+      "new_tab_page/modules/task_module/task_module_handler.h",
+      "new_tab_page/modules/task_module/task_module_service.cc",
+      "new_tab_page/modules/task_module/task_module_service.h",
+      "new_tab_page/modules/task_module/task_module_service_factory.cc",
+      "new_tab_page/modules/task_module/task_module_service_factory.h",
+      "new_tab_page/one_google_bar/one_google_bar_data.cc",
+      "new_tab_page/one_google_bar/one_google_bar_data.h",
+      "new_tab_page/one_google_bar/one_google_bar_loader.h",
+      "new_tab_page/one_google_bar/one_google_bar_loader_impl.cc",
+      "new_tab_page/one_google_bar/one_google_bar_loader_impl.h",
+      "new_tab_page/one_google_bar/one_google_bar_service.cc",
+      "new_tab_page/one_google_bar/one_google_bar_service.h",
+      "new_tab_page/one_google_bar/one_google_bar_service_factory.cc",
+      "new_tab_page/one_google_bar/one_google_bar_service_factory.h",
+      "new_tab_page/one_google_bar/one_google_bar_service_observer.h",
       "notifications/muted_notification_handler.cc",
       "notifications/muted_notification_handler.h",
       "notifications/profile_notification.cc",
@@ -3941,12 +3967,6 @@
       "search/chrome_colors/chrome_colors_factory.h",
       "search/chrome_colors/chrome_colors_service.cc",
       "search/chrome_colors/chrome_colors_service.h",
-      "search/drive/drive_handler.cc",
-      "search/drive/drive_handler.h",
-      "search/drive/drive_service.cc",
-      "search/drive/drive_service.h",
-      "search/drive/drive_service_factory.cc",
-      "search/drive/drive_service_factory.h",
       "search/instant_service.cc",
       "search/instant_service.h",
       "search/instant_service_factory.cc",
@@ -3957,16 +3977,6 @@
       "search/most_visited_iframe_source.h",
       "search/ntp_custom_background_enabled_policy_handler.cc",
       "search/ntp_custom_background_enabled_policy_handler.h",
-      "search/one_google_bar/one_google_bar_data.cc",
-      "search/one_google_bar/one_google_bar_data.h",
-      "search/one_google_bar/one_google_bar_loader.h",
-      "search/one_google_bar/one_google_bar_loader_impl.cc",
-      "search/one_google_bar/one_google_bar_loader_impl.h",
-      "search/one_google_bar/one_google_bar_service.cc",
-      "search/one_google_bar/one_google_bar_service.h",
-      "search/one_google_bar/one_google_bar_service_factory.cc",
-      "search/one_google_bar/one_google_bar_service_factory.h",
-      "search/one_google_bar/one_google_bar_service_observer.h",
       "search/promos/promo_data.cc",
       "search/promos/promo_data.h",
       "search/promos/promo_service.cc",
@@ -3986,12 +3996,6 @@
       "search/search_suggest/search_suggest_service_factory.cc",
       "search/search_suggest/search_suggest_service_factory.h",
       "search/search_suggest/search_suggest_service_observer.h",
-      "search/task_module/task_module_handler.cc",
-      "search/task_module/task_module_handler.h",
-      "search/task_module/task_module_service.cc",
-      "search/task_module/task_module_service.h",
-      "search/task_module/task_module_service_factory.cc",
-      "search/task_module/task_module_service_factory.h",
       "send_tab_to_self/desktop_notification_handler.cc",
       "send_tab_to_self/desktop_notification_handler.h",
       "send_tab_to_self/send_tab_to_self_desktop_util.cc",
@@ -4348,6 +4352,10 @@
       "apps/app_service/publishers/remote_apps.h",
       "apps/app_service/publishers/standalone_browser_apps.cc",
       "apps/app_service/publishers/standalone_browser_apps.h",
+      "apps/app_service/publishers/standalone_browser_extension_apps.cc",
+      "apps/app_service/publishers/standalone_browser_extension_apps.h",
+      "apps/app_service/publishers/standalone_browser_extension_apps_factory.cc",
+      "apps/app_service/publishers/standalone_browser_extension_apps_factory.h",
       "apps/app_service/publishers/web_apps_crosapi.cc",
       "apps/app_service/publishers/web_apps_crosapi.h",
       "apps/app_service/publishers/web_apps_crosapi_factory.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 57028b3..8739ff6 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -6806,12 +6806,10 @@
      FEATURE_VALUE_TYPE(language::kDesktopDetailedLanguageSettings)},
 #endif
 
-#if defined(OS_ANDROID)
     {"pwa-update-dialog-for-name-and-icon",
      flag_descriptions::kPwaUpdateDialogForNameAndIconName,
-     flag_descriptions::kPwaUpdateDialogForNameAndIconDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(chrome::android::kPwaUpdateDialogForNameAndIcon)},
-#endif
+     flag_descriptions::kPwaUpdateDialogForNameAndIconDescription, kOsAll,
+     FEATURE_VALUE_TYPE(features::kPwaUpdateDialogForNameAndIcon)},
 
     {"sync-autofill-wallet-offer-data",
      flag_descriptions::kSyncAutofillWalletOfferDataName,
@@ -7026,6 +7024,9 @@
 #endif
 
 #if BUILDFLAG(ENABLE_PDF)
+    {"pdf-unseasoned", flag_descriptions::kPdfUnseasonedName,
+     flag_descriptions::kPdfUnseasonedDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(chrome_pdf::features::kPdfUnseasoned)},
     {"pdf-xfa-forms", flag_descriptions::kPdfXfaFormsName,
      flag_descriptions::kPdfXfaFormsDescription, kOsAll,
      FEATURE_VALUE_TYPE(chrome_pdf::features::kPdfXfaSupport)},
diff --git a/chrome/browser/android/browserservices/metrics/BUILD.gn b/chrome/browser/android/browserservices/metrics/BUILD.gn
index 16ba8eb..1400bbb7 100644
--- a/chrome/browser/android/browserservices/metrics/BUILD.gn
+++ b/chrome/browser/android/browserservices/metrics/BUILD.gn
@@ -8,6 +8,7 @@
   sources = [
     "java/src/org/chromium/chrome/browser/browserservices/metrics/BrowserServicesTimingMetrics.java",
     "java/src/org/chromium/chrome/browser/browserservices/metrics/OriginVerifierMetricsRecorder.java",
+    "java/src/org/chromium/chrome/browser/browserservices/metrics/WebApkUkmRecorder.java",
     "java/src/org/chromium/chrome/browser/browserservices/metrics/WebApkUmaRecorder.java",
   ]
   deps = [
@@ -16,4 +17,9 @@
     "//components/browser_ui/util/android:java",
     "//third_party/androidx:androidx_annotation_annotation_java",
   ]
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+}
+
+generate_jni("jni_headers") {
+  sources = [ "java/src/org/chromium/chrome/browser/browserservices/metrics/WebApkUkmRecorder.java" ]
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUkmRecorder.java b/chrome/browser/android/browserservices/metrics/java/src/org/chromium/chrome/browser/browserservices/metrics/WebApkUkmRecorder.java
similarity index 96%
rename from chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUkmRecorder.java
rename to chrome/browser/android/browserservices/metrics/java/src/org/chromium/chrome/browser/browserservices/metrics/WebApkUkmRecorder.java
index 7ff53d5..2ea332d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUkmRecorder.java
+++ b/chrome/browser/android/browserservices/metrics/java/src/org/chromium/chrome/browser/browserservices/metrics/WebApkUkmRecorder.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.webapps;
+package org.chromium.chrome.browser.browserservices.metrics;
 
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.components.webapps.WebApkDistributor;
diff --git a/chrome/browser/android/webapk/webapk_ukm_recorder.cc b/chrome/browser/android/webapk/webapk_ukm_recorder.cc
index f42b130..91f9937 100644
--- a/chrome/browser/android/webapk/webapk_ukm_recorder.cc
+++ b/chrome/browser/android/webapk/webapk_ukm_recorder.cc
@@ -8,7 +8,7 @@
 
 #include "base/android/jni_string.h"
 #include "base/numerics/ranges.h"
-#include "chrome/android/chrome_jni_headers/WebApkUkmRecorder_jni.h"
+#include "chrome/browser/android/browserservices/metrics/jni_headers/WebApkUkmRecorder_jni.h"
 #include "components/webapps/browser/android/webapk/webapk_types.h"
 #include "services/metrics/public/cpp/metrics_utils.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
diff --git a/chrome/browser/apps/app_service/app_platform_metrics.cc b/chrome/browser/apps/app_service/app_platform_metrics.cc
index 531ab04e..4ba705a 100644
--- a/chrome/browser/apps/app_service/app_platform_metrics.cc
+++ b/chrome/browser/apps/app_service/app_platform_metrics.cc
@@ -69,6 +69,7 @@
     app_type_name_map->insert(apps::AppTypeName::kBorealis);
     app_type_name_map->insert(apps::AppTypeName::kSystemWeb);
     app_type_name_map->insert(apps::AppTypeName::kChromeBrowser);
+    app_type_name_map->insert(apps::AppTypeName::kStandaloneBrowserExtension);
   }
   return *app_type_name_map;
 }
@@ -190,6 +191,8 @@
       return apps::AppTypeName::kBorealis;
     case apps::mojom::AppType::kSystemWeb:
       return apps::AppTypeName::kSystemWeb;
+    case apps::mojom::AppType::kStandaloneBrowserExtension:
+      return apps::AppTypeName::kStandaloneBrowserExtension;
   }
 }
 
@@ -272,6 +275,8 @@
       return apps::AppTypeName::kBorealis;
     case apps::mojom::AppType::kSystemWeb:
       return apps::AppTypeName::kSystemWeb;
+    case apps::mojom::AppType::kStandaloneBrowserExtension:
+      return apps::AppTypeName::kStandaloneBrowserExtension;
   }
 }
 
@@ -312,6 +317,8 @@
 constexpr char kBorealisHistogramName[] = "Borealis";
 constexpr char kSystemWebAppHistogramName[] = "SystemWebApp";
 constexpr char kChromeBrowserHistogramName[] = "ChromeBrowser";
+constexpr char kStandaloneBrowserExtensionHistogramName[] =
+    "StandaloneBrowserExtension";
 
 std::string GetAppTypeHistogramName(apps::AppTypeName app_type_name) {
   switch (app_type_name) {
@@ -341,6 +348,8 @@
       return kSystemWebAppHistogramName;
     case apps::AppTypeName::kChromeBrowser:
       return kChromeBrowserHistogramName;
+    case apps::AppTypeName::kStandaloneBrowserExtension:
+      return kStandaloneBrowserExtensionHistogramName;
   }
 }
 
diff --git a/chrome/browser/apps/app_service/app_platform_metrics.h b/chrome/browser/apps/app_service/app_platform_metrics.h
index 9ebbf8a..1d3f0414 100644
--- a/chrome/browser/apps/app_service/app_platform_metrics.h
+++ b/chrome/browser/apps/app_service/app_platform_metrics.h
@@ -36,10 +36,11 @@
   kBorealis = 10,
   kSystemWeb = 11,
   kChromeBrowser = 12,
+  kStandaloneBrowserExtension = 13,
 
   // Add any new values above this one, and update kMaxValue to the highest
   // enumerator value.
-  kMaxValue = kChromeBrowser,
+  kMaxValue = kStandaloneBrowserExtension,
 };
 
 extern const char kAppRunningDuration[];
diff --git a/chrome/browser/apps/app_service/app_platform_metrics_service_unittest.cc b/chrome/browser/apps/app_service/app_platform_metrics_service_unittest.cc
index e395de1..0b0ca94 100644
--- a/chrome/browser/apps/app_service/app_platform_metrics_service_unittest.cc
+++ b/chrome/browser/apps/app_service/app_platform_metrics_service_unittest.cc
@@ -130,6 +130,9 @@
         /*app_id=*/"l", apps::mojom::AppType::kStandaloneBrowser,
         apps::mojom::InstallSource::kSystem));
     deltas.push_back(MakeApp(
+        /*app_id=*/"lcr", apps::mojom::AppType::kStandaloneBrowserExtension,
+        apps::mojom::InstallSource::kUser));
+    deltas.push_back(MakeApp(
         /*app_id=*/"r", apps::mojom::AppType::kRemote,
         apps::mojom::InstallSource::kPolicy));
     deltas.push_back(MakeApp(/*app_id=*/"bo", apps::mojom::AppType::kBorealis,
@@ -213,6 +216,15 @@
         /*expected_count=*/1);
     histogram_tester_.ExpectTotalCount(
         AppPlatformMetrics::GetAppsCountHistogramNameForTest(
+            AppTypeName::kStandaloneBrowserExtension),
+        /*expected_count=*/1);
+    histogram_tester_.ExpectTotalCount(
+        AppPlatformMetrics::GetAppsCountPerInstallSourceHistogramNameForTest(
+            AppTypeName::kStandaloneBrowserExtension,
+            apps::mojom::InstallSource::kUser),
+        /*expected_count=*/1);
+    histogram_tester_.ExpectTotalCount(
+        AppPlatformMetrics::GetAppsCountHistogramNameForTest(
             AppTypeName::kRemote),
         /*expected_count=*/1);
     histogram_tester_.ExpectTotalCount(
diff --git a/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.cc b/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.cc
new file mode 100644
index 0000000..3e0b703
--- /dev/null
+++ b/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.cc
@@ -0,0 +1,65 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.h"
+
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/ash/crosapi/browser_util.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
+
+namespace apps {
+
+StandaloneBrowserExtensionApps::StandaloneBrowserExtensionApps(
+    Profile* profile) {
+  apps::AppServiceProxyChromeOs* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile);
+  mojo::Remote<apps::mojom::AppService>& app_service = proxy->AppService();
+  if (!app_service.is_bound()) {
+    return;
+  }
+  PublisherBase::Initialize(app_service,
+                            apps::mojom::AppType::kStandaloneBrowserExtension);
+}
+
+StandaloneBrowserExtensionApps::~StandaloneBrowserExtensionApps() = default;
+
+void StandaloneBrowserExtensionApps::Connect(
+    mojo::PendingRemote<apps::mojom::Subscriber> subscriber_remote,
+    apps::mojom::ConnectOptionsPtr opts) {
+  mojo::Remote<apps::mojom::Subscriber> subscriber(
+      std::move(subscriber_remote));
+
+  subscribers_.Add(std::move(subscriber));
+  // TODO(https://crbug.com/1225848): Call subscriber->OnApps() with the set of
+  // registered apps.
+}
+
+void StandaloneBrowserExtensionApps::LoadIcon(const std::string& app_id,
+                                              apps::mojom::IconKeyPtr icon_key,
+                                              apps::mojom::IconType icon_type,
+                                              int32_t size_hint_in_dip,
+                                              bool allow_placeholder_icon,
+                                              LoadIconCallback callback) {
+  // TODO(https://crbug.com/1225848): Implement.
+  std::move(callback).Run(apps::mojom::IconValue::New());
+}
+
+void StandaloneBrowserExtensionApps::Launch(
+    const std::string& app_id,
+    int32_t event_flags,
+    apps::mojom::LaunchSource launch_source,
+    apps::mojom::WindowInfoPtr window_info) {
+  // TODO(https://crbug.com/1225848): Implement.
+}
+
+void StandaloneBrowserExtensionApps::GetMenuModel(
+    const std::string& app_id,
+    apps::mojom::MenuType menu_type,
+    int64_t display_id,
+    GetMenuModelCallback callback) {
+  // TODO(https://crbug.com/1225848): Implement.
+}
+
+}  // namespace apps
diff --git a/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.h b/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.h
new file mode 100644
index 0000000..0e8482a4
--- /dev/null
+++ b/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.h
@@ -0,0 +1,70 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_APPS_APP_SERVICE_PUBLISHERS_STANDALONE_BROWSER_EXTENSION_APPS_H_
+#define CHROME_BROWSER_APPS_APP_SERVICE_PUBLISHERS_STANDALONE_BROWSER_EXTENSION_APPS_H_
+
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/services/app_service/public/cpp/publisher_base.h"
+#include "components/services/app_service/public/mojom/app_service.mojom-forward.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/cpp/bindings/remote_set.h"
+
+class Profile;
+
+namespace apps {
+
+// An app publisher (in the App Service sense) for extension-based apps [e.g.
+// packaged v2 apps] published by Lacros.
+//
+// The App Service is responsible for integrating various app platforms [ARC,
+// Lacros, Crostini, etc.] with system UI: shelf, launcher, etc. The App Service
+// expects publishers to implement a uniform API, roughly translating to:
+// GetLoadedApps, LaunchApp, GetIconForApp, etc.
+//
+// This class implements this functionality for extension based apps running in
+// the Lacros context. Aforementioned API is implemented using crosapi to
+// communicate between this class and Lacros.
+//
+// The current implementation of this class relies on the assumption that Lacros
+// is almost always running in the background. This is enforced via
+// ScopedKeepAlive. We would like to eventually remove this assumption. This
+// requires caching a copy of installed apps in this class.
+class StandaloneBrowserExtensionApps : public KeyedService,
+                                       public apps::PublisherBase {
+ public:
+  explicit StandaloneBrowserExtensionApps(Profile* profile);
+  ~StandaloneBrowserExtensionApps() override;
+
+  StandaloneBrowserExtensionApps(const StandaloneBrowserExtensionApps&) =
+      delete;
+  StandaloneBrowserExtensionApps& operator=(
+      const StandaloneBrowserExtensionApps&) = delete;
+
+ private:
+  // apps::PublisherBase:
+  void Connect(mojo::PendingRemote<apps::mojom::Subscriber> subscriber_remote,
+               apps::mojom::ConnectOptionsPtr opts) override;
+  void LoadIcon(const std::string& app_id,
+                apps::mojom::IconKeyPtr icon_key,
+                apps::mojom::IconType icon_type,
+                int32_t size_hint_in_dip,
+                bool allow_placeholder_icon,
+                LoadIconCallback callback) override;
+  void Launch(const std::string& app_id,
+              int32_t event_flags,
+              apps::mojom::LaunchSource launch_source,
+              apps::mojom::WindowInfoPtr window_info) override;
+  void GetMenuModel(const std::string& app_id,
+                    apps::mojom::MenuType menu_type,
+                    int64_t display_id,
+                    GetMenuModelCallback callback) override;
+
+  mojo::RemoteSet<apps::mojom::Subscriber> subscribers_;
+};
+
+}  // namespace apps
+
+#endif  // CHROME_BROWSER_APPS_APP_SERVICE_PUBLISHERS_STANDALONE_BROWSER_EXTENSION_APPS_H_
diff --git a/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps_factory.cc b/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps_factory.cc
new file mode 100644
index 0000000..bd3fc07
--- /dev/null
+++ b/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps_factory.cc
@@ -0,0 +1,49 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps_factory.h"
+
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+
+namespace apps {
+
+// static
+StandaloneBrowserExtensionApps*
+StandaloneBrowserExtensionAppsFactory::GetForProfile(Profile* profile) {
+  return static_cast<StandaloneBrowserExtensionApps*>(
+      StandaloneBrowserExtensionAppsFactory::GetInstance()
+          ->GetServiceForBrowserContext(profile, true /* create */));
+}
+
+// static
+StandaloneBrowserExtensionAppsFactory*
+StandaloneBrowserExtensionAppsFactory::GetInstance() {
+  return base::Singleton<StandaloneBrowserExtensionAppsFactory>::get();
+}
+
+// static
+void StandaloneBrowserExtensionAppsFactory::ShutDownForTesting(
+    content::BrowserContext* context) {
+  auto* factory = GetInstance();
+  factory->BrowserContextShutdown(context);
+  factory->BrowserContextDestroyed(context);
+}
+
+StandaloneBrowserExtensionAppsFactory::StandaloneBrowserExtensionAppsFactory()
+    : BrowserContextKeyedServiceFactory(
+          "StandaloneBrowserExtensionApps",
+          BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(apps::AppServiceProxyFactory::GetInstance());
+}
+
+KeyedService* StandaloneBrowserExtensionAppsFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  return new StandaloneBrowserExtensionApps(
+      Profile::FromBrowserContext(context));
+}
+
+}  // namespace apps
diff --git a/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps_factory.h b/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps_factory.h
new file mode 100644
index 0000000..802d3dfa1
--- /dev/null
+++ b/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps_factory.h
@@ -0,0 +1,46 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_APPS_APP_SERVICE_PUBLISHERS_STANDALONE_BROWSER_EXTENSION_APPS_FACTORY_H_
+#define CHROME_BROWSER_APPS_APP_SERVICE_PUBLISHERS_STANDALONE_BROWSER_EXTENSION_APPS_FACTORY_H_
+
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class Profile;
+
+namespace apps {
+
+class StandaloneBrowserExtensionApps;
+
+// Singleton that owns all StandaloneBrowserExtensionApps publisher and
+// associates them with Profiles.
+class StandaloneBrowserExtensionAppsFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  static StandaloneBrowserExtensionApps* GetForProfile(Profile* profile);
+
+  static StandaloneBrowserExtensionAppsFactory* GetInstance();
+
+  static void ShutDownForTesting(content::BrowserContext* context);
+
+ private:
+  friend struct base::DefaultSingletonTraits<
+      StandaloneBrowserExtensionAppsFactory>;
+
+  StandaloneBrowserExtensionAppsFactory();
+  StandaloneBrowserExtensionAppsFactory(
+      const StandaloneBrowserExtensionAppsFactory&) = delete;
+  StandaloneBrowserExtensionAppsFactory& operator=(
+      const StandaloneBrowserExtensionAppsFactory&) = delete;
+  ~StandaloneBrowserExtensionAppsFactory() override = default;
+
+  // BrowserContextKeyedServiceFactory overrides.
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+};
+
+}  // namespace apps
+
+#endif  // CHROME_BROWSER_APPS_APP_SERVICE_PUBLISHERS_STANDALONE_BROWSER_EXTENSION_APPS_FACTORY_H_
diff --git a/chrome/browser/ash/apps/intent_helper/ash_intent_picker_helpers.cc b/chrome/browser/ash/apps/intent_helper/ash_intent_picker_helpers.cc
index eb62dca8..5311d55 100644
--- a/chrome/browser/ash/apps/intent_helper/ash_intent_picker_helpers.cc
+++ b/chrome/browser/ash/apps/intent_helper/ash_intent_picker_helpers.cc
@@ -277,6 +277,7 @@
     case mojom::AppType::kPluginVm:
     case mojom::AppType::kExtension:
     case mojom::AppType::kStandaloneBrowser:
+    case mojom::AppType::kStandaloneBrowserExtension:
     case mojom::AppType::kRemote:
     case mojom::AppType::kBorealis:
       break;
diff --git a/chrome/browser/ash/child_accounts/family_user_app_metrics.cc b/chrome/browser/ash/child_accounts/family_user_app_metrics.cc
index 1a17ff4..86b01a33 100644
--- a/chrome/browser/ash/child_accounts/family_user_app_metrics.cc
+++ b/chrome/browser/ash/child_accounts/family_user_app_metrics.cc
@@ -56,6 +56,8 @@
     "FamilyUser.BorealisAppsCount2";
 constexpr char kSystemWebAppsCountHistogramName[] =
     "FamilyUser.SystemWebAppsCount2";
+constexpr char kStandaloneBrowserExtensionCountHistogramName[] =
+    "FamilyUser.LacrosChromeAppsCount2";
 
 const char* GetAppsCountHistogramName(apps::mojom::AppType app_type) {
   switch (app_type) {
@@ -83,6 +85,8 @@
       return kBorealisAppsCountHistogramName;
     case apps::mojom::AppType::kSystemWeb:
       return kSystemWebAppsCountHistogramName;
+    case apps::mojom::AppType::kStandaloneBrowserExtension:
+      return kStandaloneBrowserExtensionCountHistogramName;
   }
 }
 
diff --git a/chrome/browser/ash/child_accounts/family_user_app_metrics_unittest.cc b/chrome/browser/ash/child_accounts/family_user_app_metrics_unittest.cc
index e21741a0b..e5651da3 100644
--- a/chrome/browser/ash/child_accounts/family_user_app_metrics_unittest.cc
+++ b/chrome/browser/ash/child_accounts/family_user_app_metrics_unittest.cc
@@ -178,6 +178,10 @@
         /*last_launch_time=*/base::Time::Now() - kOneDay,
         apps::mojom::AppType::kStandaloneBrowser));
     deltas.push_back(MakeApp(
+        /*app_id=*/"lca", /*app_name=*/"lacros chrome app",
+        /*last_launch_time=*/base::Time::Now() - kOneDay,
+        apps::mojom::AppType::kStandaloneBrowserExtension));
+    deltas.push_back(MakeApp(
         /*app_id=*/"r", /*app_name=*/"remote",
         /*last_launch_time=*/base::Time::Now() - kOneDay,
         apps::mojom::AppType::kRemote));
diff --git a/chrome/browser/ash/child_accounts/time_limits/app_types.cc b/chrome/browser/ash/child_accounts/time_limits/app_types.cc
index 9a30892..637de87 100644
--- a/chrome/browser/ash/child_accounts/time_limits/app_types.cc
+++ b/chrome/browser/ash/child_accounts/time_limits/app_types.cc
@@ -23,6 +23,7 @@
     case apps::mojom::AppType::kWeb:
       return "Web";
     case apps::mojom::AppType::kExtension:
+    case apps::mojom::AppType::kStandaloneBrowserExtension:
       return "Extension";
     case apps::mojom::AppType::kBuiltIn:
       return "Built in";
diff --git a/chrome/browser/ash/crosapi/crosapi_id.h b/chrome/browser/ash/crosapi/crosapi_id.h
index 072f543..38792eb 100644
--- a/chrome/browser/ash/crosapi/crosapi_id.h
+++ b/chrome/browser/ash/crosapi/crosapi_id.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_ASH_CROSAPI_CROSAPI_ID_H_
 #define CHROME_BROWSER_ASH_CROSAPI_CROSAPI_ID_H_
 
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 
 namespace crosapi {
 namespace internal {
@@ -15,7 +15,7 @@
 // CrosapiId is an id created on a new Crosapi connection creation.
 // This will be useful to identify what bindings/remote of sub crosapi
 // interfaces are related each other.
-using CrosapiId = util::IdTypeU32<internal::CrosapiIdTag>;
+using CrosapiId = base::IdTypeU32<internal::CrosapiIdTag>;
 
 }  // namespace crosapi
 
diff --git a/chrome/browser/ash/file_manager/app_service_file_tasks.cc b/chrome/browser/ash/file_manager/app_service_file_tasks.cc
index c1159a5..86aa1ae 100644
--- a/chrome/browser/ash/file_manager/app_service_file_tasks.cc
+++ b/chrome/browser/ash/file_manager/app_service_file_tasks.cc
@@ -56,6 +56,7 @@
     case apps::mojom::AppType::kStandaloneBrowser:
     case apps::mojom::AppType::kRemote:
     case apps::mojom::AppType::kBorealis:
+    case apps::mojom::AppType::kStandaloneBrowserExtension:
       return TASK_TYPE_UNKNOWN;
   }
 }
diff --git a/chrome/browser/ash/login/auth/chrome_safe_mode_delegate.cc b/chrome/browser/ash/login/auth/chrome_safe_mode_delegate.cc
index 73eacb9..cabcd75 100644
--- a/chrome/browser/ash/login/auth/chrome_safe_mode_delegate.cc
+++ b/chrome/browser/ash/login/auth/chrome_safe_mode_delegate.cc
@@ -7,7 +7,6 @@
 #include "chrome/browser/ash/ownership/owner_settings_service_ash.h"
 #include "chrome/browser/ash/ownership/owner_settings_service_ash_factory.h"
 #include "chrome/browser/ash/settings/cros_settings.h"
-#include "chromeos/login/auth/user_context.h"
 #include "chromeos/login/login_state/login_state.h"
 
 namespace ash {
@@ -18,8 +17,9 @@
   return is_safe_mode;
 }
 
-void ChromeSafeModeDelegate::CheckSafeModeOwnership(const UserContext& context,
-                                                    IsOwnerCallback callback) {
+void ChromeSafeModeDelegate::CheckSafeModeOwnership(
+    const std::string& user_id_hash,
+    IsOwnerCallback callback) {
   // `IsOwnerForSafeModeAsync` expects logged in state to be
   // LOGGED_IN_SAFE_MODE.
   if (LoginState::IsInitialized()) {
@@ -28,7 +28,7 @@
   }
 
   OwnerSettingsServiceAsh::IsOwnerForSafeModeAsync(
-      context.GetUserIDHash(),
+      user_id_hash,
       OwnerSettingsServiceAshFactory::GetInstance()->GetOwnerKeyUtil(),
       std::move(callback));
 }
diff --git a/chrome/browser/ash/login/auth/chrome_safe_mode_delegate.h b/chrome/browser/ash/login/auth/chrome_safe_mode_delegate.h
index c6be1bc..88fbb15 100644
--- a/chrome/browser/ash/login/auth/chrome_safe_mode_delegate.h
+++ b/chrome/browser/ash/login/auth/chrome_safe_mode_delegate.h
@@ -6,8 +6,6 @@
 #define CHROME_BROWSER_ASH_LOGIN_AUTH_CHROME_SAFE_MODE_DELEGATE_H_
 
 #include "chromeos/login/auth/safe_mode_delegate.h"
-// TODO(https://crbug.com/1164001): move to forward declaration
-#include "chromeos/login/auth/user_context.h"
 
 namespace ash {
 
@@ -21,7 +19,7 @@
   ChromeSafeModeDelegate& operator=(const ChromeSafeModeDelegate&) = delete;
 
   bool IsSafeMode() override;
-  void CheckSafeModeOwnership(const UserContext& context,
+  void CheckSafeModeOwnership(const std::string& user_id_hash,
                               IsOwnerCallback callback) override;
 };
 
diff --git a/chrome/browser/autofill/autofill_image_fetcher_factory.cc b/chrome/browser/autofill/autofill_image_fetcher_factory.cc
new file mode 100644
index 0000000..1c65655
--- /dev/null
+++ b/chrome/browser/autofill/autofill_image_fetcher_factory.cc
@@ -0,0 +1,59 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/autofill/autofill_image_fetcher_factory.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/image_fetcher/image_decoder_impl.h"
+#include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/autofill/core/browser/ui/autofill_image_fetcher.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "content/public/browser/storage_partition.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+namespace autofill {
+
+// static
+AutofillImageFetcher* AutofillImageFetcherFactory::GetForBrowserContext(
+    content::BrowserContext* context) {
+  return static_cast<AutofillImageFetcher*>(
+      GetInstance()->GetServiceForBrowserContext(context, true));
+}
+
+// static
+AutofillImageFetcherFactory* AutofillImageFetcherFactory::GetInstance() {
+  static base::NoDestructor<AutofillImageFetcherFactory> instance;
+  return instance.get();
+}
+
+AutofillImageFetcherFactory::AutofillImageFetcherFactory()
+    : BrowserContextKeyedServiceFactory(
+          "AutofillImageFetcher",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+AutofillImageFetcherFactory::~AutofillImageFetcherFactory() = default;
+
+KeyedService* AutofillImageFetcherFactory::BuildAutofillImageFetcher(
+    content::BrowserContext* context) {
+  Profile* profile = Profile::FromBrowserContext(context);
+  AutofillImageFetcher* service =
+      new AutofillImageFetcher(profile->GetDefaultStoragePartition()
+                                   ->GetURLLoaderFactoryForBrowserProcess(),
+                               std::make_unique<ImageDecoderImpl>());
+  return service;
+}
+
+KeyedService* AutofillImageFetcherFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  return BuildAutofillImageFetcher(context);
+}
+
+content::BrowserContext* AutofillImageFetcherFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  // Use the image fetcher from the original browser context.
+  return chrome::GetBrowserContextRedirectedInIncognito(context);
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/autofill/autofill_image_fetcher_factory.h b/chrome/browser/autofill/autofill_image_fetcher_factory.h
new file mode 100644
index 0000000..e9b73a4
--- /dev/null
+++ b/chrome/browser/autofill/autofill_image_fetcher_factory.h
@@ -0,0 +1,43 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_AUTOFILL_AUTOFILL_IMAGE_FETCHER_FACTORY_H_
+#define CHROME_BROWSER_AUTOFILL_AUTOFILL_IMAGE_FETCHER_FACTORY_H_
+
+#include "base/no_destructor.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace autofill {
+
+class AutofillImageFetcher;
+
+class AutofillImageFetcherFactory : public BrowserContextKeyedServiceFactory {
+ public:
+  // Returns the AutofillImageFetcher for |context|, creating it if it is not
+  // yet created.
+  static AutofillImageFetcher* GetForBrowserContext(
+      content::BrowserContext* context);
+
+  static AutofillImageFetcherFactory* GetInstance();
+
+  static KeyedService* BuildAutofillImageFetcher(
+      content::BrowserContext* context);
+
+ private:
+  friend class base::NoDestructor<AutofillImageFetcherFactory>;
+
+  AutofillImageFetcherFactory();
+  ~AutofillImageFetcherFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* profile) const override;
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+};
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_AUTOFILL_AUTOFILL_IMAGE_FETCHER_FACTORY_H_
diff --git a/chrome/browser/browser_features.cc b/chrome/browser/browser_features.cc
index dcc255d..8f75874 100644
--- a/chrome/browser/browser_features.cc
+++ b/chrome/browser/browser_features.cc
@@ -47,6 +47,11 @@
     "MuteNotificationSnoozeAction", base::FEATURE_DISABLED_BY_DEFAULT};
 #endif
 
+// Shows a confirmation dialog when updates to PWAs identity (name and icon)
+// have been detected.
+const base::Feature kPwaUpdateDialogForNameAndIcon{
+    "pwa-update-dialog-for-name-and-icon", base::FEATURE_DISABLED_BY_DEFAULT};
+
 #if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
 // Enables taking snapshots of the user data directory after a major
 // milestone update and restoring them after a version rollback.
diff --git a/chrome/browser/browser_features.h b/chrome/browser/browser_features.h
index e6832e47..7a0a22d9 100644
--- a/chrome/browser/browser_features.h
+++ b/chrome/browser/browser_features.h
@@ -33,6 +33,8 @@
 extern const base::Feature kMuteNotificationSnoozeAction;
 #endif
 
+extern const base::Feature kPwaUpdateDialogForNameAndIcon;
+
 #if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
 extern const base::Feature kUserDataSnapshot;
 #endif
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 0c90b3a3..cef91e9b 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -106,10 +106,10 @@
 #include "chrome/browser/badging/badge_manager.h"
 #include "chrome/browser/cart/chrome_cart.mojom.h"
 #include "chrome/browser/cart/commerce_hint_service.h"
+#include "chrome/browser/new_tab_page/modules/drive/drive.mojom.h"
+#include "chrome/browser/new_tab_page/modules/task_module/task_module.mojom.h"
 #include "chrome/browser/payments/payment_request_factory.h"
 #include "chrome/browser/promo_browser_command/promo_browser_command.mojom.h"
-#include "chrome/browser/search/drive/drive.mojom.h"
-#include "chrome/browser/search/task_module/task_module.mojom.h"
 #include "chrome/browser/speech/speech_recognition_client_browser_interface.h"
 #include "chrome/browser/speech/speech_recognition_client_browser_interface_factory.h"
 #include "chrome/browser/speech/speech_recognition_service.h"
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 3252746..d0f4e61 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -4236,12 +4236,12 @@
       &throttles);
 #endif
 
-  // TODO(crbug.com/1218526): Pass in a ChromeSecurityBlockingPageFactory so
-  // that the nav throttle can create interstitials.
   if (profile && profile->GetPrefs()) {
-    MaybeAddThrottle(HttpsOnlyModeNavigationThrottle::MaybeCreateThrottleFor(
-                         handle, profile->GetPrefs()),
-                     &throttles);
+    MaybeAddThrottle(
+        HttpsOnlyModeNavigationThrottle::MaybeCreateThrottleFor(
+            handle, std::make_unique<ChromeSecurityBlockingPageFactory>(),
+            profile->GetPrefs()),
+        &throttles);
   }
 
   return throttles;
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index 02bbc8a..c59f032 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -341,6 +341,11 @@
       return api::autotest_private::AppType::APP_TYPE_REMOTE;
     case apps::mojom::AppType::kBorealis:
       return api::autotest_private::AppType::APP_TYPE_BOREALIS;
+    case apps::mojom::AppType::kStandaloneBrowserExtension:
+      // Intentionally fall-through for now.
+      // TODO(https://crbug.com/1225848): Figure out appropriate behavior for
+      // Lacros-hosted chrome-apps.
+      break;
   }
   NOTREACHED();
   return api::autotest_private::AppType::APP_TYPE_NONE;
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
index d31a87b..704839a 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
+++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
@@ -14,6 +14,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "chrome/browser/profiles/profiles_state.h"
+#include "chrome/common/extensions/api/chrome_url_overrides.h"
 #include "chrome/common/extensions/api/omnibox.h"
 #include "chrome/grit/generated_resources.h"
 #include "extensions/browser/device_local_account_util.h"
@@ -72,7 +73,7 @@
     // emk::kSettingsOverride,
 
     // Bookmark manager, history, new tab - should be safe.
-    emk::kChromeURLOverrides,
+    ext_api::chrome_url_overrides::ManifestKeys::kChromeUrlOverrides,
 
     // General risk of capturing user input, but key combos must include Ctrl or
     // Alt, so I think this is safe.
diff --git a/chrome/browser/chromeos/full_restore/app_launch_handler.cc b/chrome/browser/chromeos/full_restore/app_launch_handler.cc
index faf82c1..2242264 100644
--- a/chrome/browser/chromeos/full_restore/app_launch_handler.cc
+++ b/chrome/browser/chromeos/full_restore/app_launch_handler.cc
@@ -40,6 +40,7 @@
     case apps::mojom::AppType::kMacOs:
     case apps::mojom::AppType::kPluginVm:
     case apps::mojom::AppType::kStandaloneBrowser:
+    case apps::mojom::AppType::kStandaloneBrowserExtension:
     case apps::mojom::AppType::kRemote:
     case apps::mojom::AppType::kBorealis:
       return apps::AppTypeName::kUnknown;
@@ -137,9 +138,9 @@
 
   switch (app_type) {
     case apps::mojom::AppType::kArc:
-      LaunchArcApp(app_id, it->second);
-      // ARC apps restoration could be delayed, so return to preserve the
-      // restore data for ARC apps.
+      // ArcAppLaunchHandler handles ARC apps restoration and ARC apps
+      // restoration could be delayed, so return to preserve the restore data
+      // for ARC apps.
       return;
     case apps::mojom::AppType::kExtension:
       ::full_restore::FullRestoreReadHandler::GetInstance()
@@ -157,6 +158,7 @@
     case apps::mojom::AppType::kUnknown:
     case apps::mojom::AppType::kMacOs:
     case apps::mojom::AppType::kStandaloneBrowser:
+    case apps::mojom::AppType::kStandaloneBrowserExtension:
     case apps::mojom::AppType::kRemote:
     case apps::mojom::AppType::kBorealis:
       NOTREACHED();
diff --git a/chrome/browser/chromeos/full_restore/app_launch_handler.h b/chrome/browser/chromeos/full_restore/app_launch_handler.h
index b9de06d..a39efe8 100644
--- a/chrome/browser/chromeos/full_restore/app_launch_handler.h
+++ b/chrome/browser/chromeos/full_restore/app_launch_handler.h
@@ -52,9 +52,6 @@
       const ::full_restore::RestoreData::LaunchList& launch_list);
 
   virtual void LaunchBrowser() = 0;
-  virtual void LaunchArcApp(
-      const std::string& app_id,
-      const ::full_restore::RestoreData::LaunchList& launch_list) = 0;
   virtual void RecordRestoredAppLaunch(apps::AppTypeName app_type_name) = 0;
   virtual void RecordArcGhostWindowLaunch(bool is_arc_ghost_window) = 0;
 };
diff --git a/chrome/browser/chromeos/full_restore/full_restore_app_launch_handler.cc b/chrome/browser/chromeos/full_restore/full_restore_app_launch_handler.cc
index 8624a209..991f916 100644
--- a/chrome/browser/chromeos/full_restore/full_restore_app_launch_handler.cc
+++ b/chrome/browser/chromeos/full_restore/full_restore_app_launch_handler.cc
@@ -175,13 +175,6 @@
   UserSessionManager::GetInstance()->LaunchBrowser(profile_);
 }
 
-void FullRestoreAppLaunchHandler::LaunchArcApp(
-    const std::string& app_id,
-    const ::full_restore::RestoreData::LaunchList& launch_list) {
-  // TODO(crbug.com/1146900): Remove this function, because the implementation
-  // has been moved to ArcAppLaunchHandler.
-}
-
 void FullRestoreAppLaunchHandler::RecordRestoredAppLaunch(
     apps::AppTypeName app_type_name) {
   base::UmaHistogramEnumeration(kRestoredAppLaunchHistogramPrefix,
diff --git a/chrome/browser/chromeos/full_restore/full_restore_app_launch_handler.h b/chrome/browser/chromeos/full_restore/full_restore_app_launch_handler.h
index 3f61b3d..3e8c42f0 100644
--- a/chrome/browser/chromeos/full_restore/full_restore_app_launch_handler.h
+++ b/chrome/browser/chromeos/full_restore/full_restore_app_launch_handler.h
@@ -74,9 +74,6 @@
 
   // AppLaunchHandler:
   void LaunchBrowser() override;
-  void LaunchArcApp(
-      const std::string& app_id,
-      const ::full_restore::RestoreData::LaunchList& launch_list) override;
   void RecordRestoredAppLaunch(apps::AppTypeName app_type_name) override;
   void RecordArcGhostWindowLaunch(bool is_arc_ghost_window) override;
 
diff --git a/chrome/browser/chromeos/policy/status_collector/app_info_generator.cc b/chrome/browser/chromeos/policy/status_collector/app_info_generator.cc
index ae71d95..1f96fc0 100644
--- a/chrome/browser/chromeos/policy/status_collector/app_info_generator.cc
+++ b/chrome/browser/chromeos/policy/status_collector/app_info_generator.cc
@@ -48,6 +48,7 @@
     case apps::mojom::AppType::kPluginVm:
       return em::AppInfo::AppType::AppInfo_AppType_TYPE_PLUGINVM;
     case apps::mojom::AppType::kExtension:
+    case apps::mojom::AppType::kStandaloneBrowserExtension:
       return em::AppInfo::AppType::AppInfo_AppType_TYPE_EXTENSION;
     case apps::mojom::AppType::kWeb:
     case apps::mojom::AppType::kSystemWeb:
diff --git a/chrome/browser/content_creation/notes/internal/android/java/src/org/chromium/chrome/browser/content_creation/notes/NoteCreationCoordinatorImpl.java b/chrome/browser/content_creation/notes/internal/android/java/src/org/chromium/chrome/browser/content_creation/notes/NoteCreationCoordinatorImpl.java
index 6833f55..7da0b9e3 100644
--- a/chrome/browser/content_creation/notes/internal/android/java/src/org/chromium/chrome/browser/content_creation/notes/NoteCreationCoordinatorImpl.java
+++ b/chrome/browser/content_creation/notes/internal/android/java/src/org/chromium/chrome/browser/content_creation/notes/NoteCreationCoordinatorImpl.java
@@ -52,6 +52,8 @@
     private final String mShareUrl;
     private final String mSelectedText;
 
+    private long mCreationStartTime;
+
     private TopBarCoordinator mTopBarCoordinator;
 
     public NoteCreationCoordinatorImpl(Activity activity, Tab tab, NoteService noteService,
@@ -82,6 +84,7 @@
 
     @Override
     public void showDialog() {
+        mCreationStartTime = System.currentTimeMillis();
         NoteCreationMetrics.recordNoteCreationSelected();
 
         FragmentActivity fragmentActivity = (FragmentActivity) mActivity;
@@ -93,7 +96,7 @@
      */
     @Override
     public void dismiss() {
-        NoteCreationMetrics.recordNoteCreationStatus(/*created=*/false);
+        NoteCreationMetrics.recordNoteCreationDismissed(getTimeElapsedSinceCreationStart());
         NoteCreationMetrics.recordNbTemplateChanges(mDialog.getNbTemplateSwitches());
         mDialog.dismiss();
     }
@@ -111,8 +114,7 @@
      */
     @Override
     public void executeAction() {
-        NoteCreationMetrics.recordNoteTemplateSelected();
-        NoteCreationMetrics.recordNoteCreationStatus(/*created=*/true);
+        NoteCreationMetrics.recordNoteTemplateSelected(getTimeElapsedSinceCreationStart());
         NoteCreationMetrics.recordNbTemplateChanges(mDialog.getNbTemplateSwitches());
 
         int selectedNoteIndex = mDialog.getSelectedItemIndex();
@@ -138,12 +140,14 @@
                                     .setCallback(new ShareParams.TargetChosenCallback() {
                                         @Override
                                         public void onTargetChosen(ComponentName chosenComponent) {
-                                            NoteCreationMetrics.recordNoteShared();
+                                            NoteCreationMetrics.recordNoteShared(
+                                                    getTimeElapsedSinceCreationStart());
                                         }
 
                                         @Override
                                         public void onCancel() {
-                                            NoteCreationMetrics.recordNoteNotShared();
+                                            NoteCreationMetrics.recordNoteNotShared(
+                                                    getTimeElapsedSinceCreationStart());
                                         }
                                     })
                                     .build();
@@ -223,4 +227,11 @@
         this.dismiss();
         mChromeOptionShareCallback.showShareSheet(params, extras, shareStartTime);
     }
+
+    /**
+     * Returns the time elapsed since the creation was started.
+     */
+    private long getTimeElapsedSinceCreationStart() {
+        return System.currentTimeMillis() - mCreationStartTime;
+    }
 }
\ No newline at end of file
diff --git a/chrome/browser/content_creation/notes/internal/android/java/src/org/chromium/chrome/browser/content_creation/notes/NoteCreationMetrics.java b/chrome/browser/content_creation/notes/internal/android/java/src/org/chromium/chrome/browser/content_creation/notes/NoteCreationMetrics.java
index a0615e4..c36c1b4 100644
--- a/chrome/browser/content_creation/notes/internal/android/java/src/org/chromium/chrome/browser/content_creation/notes/NoteCreationMetrics.java
+++ b/chrome/browser/content_creation/notes/internal/android/java/src/org/chromium/chrome/browser/content_creation/notes/NoteCreationMetrics.java
@@ -44,30 +44,74 @@
         int NUM_ENTRIES = 11;
     }
 
+    /**
+     * Records metrics related to the user starting the creation flow.
+     */
     public static void recordNoteCreationSelected() {
         RecordHistogram.recordEnumeratedHistogram("NoteCreation.Funnel",
                 NoteCreationFunnel.NOTE_CREATION_SELECTED, NoteCreationFunnel.NUM_ENTRIES);
     }
 
-    public static void recordNoteTemplateSelected() {
+    /**
+     * Records metrics related to the user choosing a template and creating their note.
+     *
+     * @param duration The time elapsed between the start of the creation flow and when the user
+     *         selected a template and created their note.
+     */
+    public static void recordNoteTemplateSelected(long duration) {
         RecordHistogram.recordEnumeratedHistogram("NoteCreation.Funnel",
                 NoteCreationFunnel.TEMPLATE_SELECTED, NoteCreationFunnel.NUM_ENTRIES);
+
+        RecordHistogram.recordBooleanHistogram("NoteCreation.CreationStatus", true);
+
+        RecordHistogram.recordMediumTimesHistogram("NoteCreation.TimeTo.SelectTemplate", duration);
     }
 
-    public static void recordNoteShared() {
+    /**
+     * Records metrics related to the user dismissing the creation dialog.
+     *
+     * @param duration The time elapsed between the start of the creation flow and when the user
+     *         dismissed the creation dialog.
+     */
+    public static void recordNoteCreationDismissed(long duration) {
+        RecordHistogram.recordBooleanHistogram("NoteCreation.CreationStatus", false);
+
+        RecordHistogram.recordMediumTimesHistogram(
+                "NoteCreation.TimeTo.DismissCreationDialog", duration);
+    }
+
+    /**
+     * Records metrics related to the user sharing their created note.
+     *
+     * @param duration The time elapsed between the start of the creation flow and when the user
+     *         shared their created note.
+     */
+    public static void recordNoteShared(long duration) {
         RecordHistogram.recordEnumeratedHistogram("NoteCreation.Funnel",
                 NoteCreationFunnel.NOTE_SHARED, NoteCreationFunnel.NUM_ENTRIES);
+
         RecordHistogram.recordBooleanHistogram("NoteCreation.NoteShared", true);
+
+        RecordHistogram.recordMediumTimesHistogram("NoteCreation.TimeTo.ShareCreation", duration);
     }
 
-    public static void recordNoteNotShared() {
+    /**
+     * Records metrics related to the user not sharing their created note.
+     *
+     * @param duration The time elapsed between the start of the creation flow and when the user
+     *         dismissed the share sheet.
+     */
+    public static void recordNoteNotShared(long duration) {
         RecordHistogram.recordBooleanHistogram("NoteCreation.NoteShared", false);
+
+        RecordHistogram.recordMediumTimesHistogram("NoteCreation.TimeTo.DismissShare", duration);
     }
 
-    public static void recordNoteCreationStatus(boolean created) {
-        RecordHistogram.recordBooleanHistogram("NoteCreation.CreationStatus", created);
-    }
-
+    /**
+     * Records the number of times the user switched between templates.
+     *
+     * @param nbChanges The number of times the user changes templates.
+     */
     public static void recordNbTemplateChanges(int nbChanges) {
         RecordHistogram.recordCount100Histogram("NoteCreation.NumberOfTemplateChanges", nbChanges);
     }
diff --git a/chrome/browser/download/notification/download_item_notification.cc b/chrome/browser/download/notification/download_item_notification.cc
index 449d280..25a33662 100644
--- a/chrome/browser/download/notification/download_item_notification.cc
+++ b/chrome/browser/download/notification/download_item_notification.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/download/download_commands.h"
 #include "chrome/browser/download/download_crx_util.h"
 #include "chrome/browser/download/download_item_model.h"
+#include "chrome/browser/download/download_stats.h"
 #include "chrome/browser/download/notification/download_notification_manager.h"
 #include "chrome/browser/enterprise/connectors/connectors_service.h"
 #include "chrome/browser/notifications/notification_display_service.h"
@@ -448,6 +449,9 @@
 
   if (item_->IsDangerous()) {
     notification_->set_type(message_center::NOTIFICATION_TYPE_BASE_FORMAT);
+    RecordDangerousDownloadWarningShown(
+        item_->GetDangerType(), item_->GetTargetFilePath(),
+        item_->GetURL().SchemeIs(url::kHttpsScheme), item_->HasUserGesture());
     if (!item_->MightBeMalicious() &&
         item_->GetDangerType() !=
             download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING) {
diff --git a/chrome/browser/download/notification/download_item_notification_unittest.cc b/chrome/browser/download/notification/download_item_notification_unittest.cc
index fdcbcb0..2bfde274 100644
--- a/chrome/browser/download/notification/download_item_notification_unittest.cc
+++ b/chrome/browser/download/notification/download_item_notification_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/json/json_reader.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/download/chrome_download_manager_delegate.h"
@@ -28,6 +29,7 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "components/download/public/common/download_danger_type.h"
 #include "components/download/public/common/mock_download_item.h"
 #include "components/enterprise/common/proto/download_item_reroute_info.pb.h"
 #include "content/public/browser/download_item_utils.h"
@@ -155,6 +157,7 @@
 };
 
 TEST_F(DownloadItemNotificationTest, ShowAndCloseNotification) {
+  base::HistogramTester histograms;
   EXPECT_EQ(0u, NotificationCount());
 
   // Shows a notification
@@ -172,6 +175,32 @@
 
   // Makes sure the DownloadItem::Cancel() is never called.
   EXPECT_CALL(*download_item_, Cancel(_)).Times(0);
+
+  // Not logged because the download is safe.
+  histograms.ExpectTotalCount("Download.ShowedDownloadWarning", 0);
+}
+
+TEST_F(DownloadItemNotificationTest, ShowAndCloseDangerousNotification) {
+  base::HistogramTester histograms;
+  EXPECT_CALL(*download_item_, GetDangerType())
+      .WillRepeatedly(Return(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT));
+  EXPECT_CALL(*download_item_, IsDangerous()).WillRepeatedly(Return(true));
+
+  // Shows a notification
+  CreateDownloadItemNotification();
+  download_item_->NotifyObserversDownloadOpened();
+  EXPECT_EQ(1u, NotificationCount());
+
+  // Closes it once.
+  RemoveNotification();
+
+  // Confirms that the notification is closed.
+  EXPECT_EQ(0u, NotificationCount());
+
+  // The download warning showed histogram is logged.
+  histograms.ExpectBucketCount("Download.ShowedDownloadWarning",
+                               download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT,
+                               1);
 }
 
 TEST_F(DownloadItemNotificationTest, PauseAndResumeNotification) {
diff --git a/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.cc b/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.cc
index 7968ef71..21fa4ba 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.cc
+++ b/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.cc
@@ -73,6 +73,23 @@
   return id;
 }
 
+std::string ExtractParentId(const base::Value& value) {
+  std::string id;
+  const base::Value* parent = nullptr;
+  const base::Value* parent_id = nullptr;
+
+  parent = value.FindPath("parent");
+
+  if (parent)
+    parent_id = parent->FindPath("id");
+  if (parent_id && parent_id->is_int())
+    id = base::NumberToString(parent_id->GetInt());
+  else
+    DLOG(ERROR) << "[BoxApiCallFlow] Parent ID not found";
+
+  return id;
+}
+
 // For possible extensions:
 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
 std::string GetMimeType(base::FilePath file_path) {
@@ -283,6 +300,48 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// GetFileFolder
+////////////////////////////////////////////////////////////////////////////////
+// BoxApiCallFlow interface.
+// API reference:
+// https://developer.box.com/reference/resources/file/
+BoxGetFileFolderApiCallFlow::BoxGetFileFolderApiCallFlow(
+    TaskCallback callback,
+    const std::string& file_id)
+    : callback_(std::move(callback)), file_id_(file_id) {}
+BoxGetFileFolderApiCallFlow::~BoxGetFileFolderApiCallFlow() = default;
+
+GURL BoxGetFileFolderApiCallFlow::CreateApiCallUrl() {
+  std::string path("2.0/files/" + file_id_);
+  return BoxApiCallFlow::CreateApiCallUrl().Resolve(path);
+}
+
+void BoxGetFileFolderApiCallFlow::ProcessApiCallSuccess(
+    const network::mojom::URLResponseHead* head,
+    std::unique_ptr<std::string> body) {
+  auto response_code = head->headers->response_code();
+  CHECK_EQ(response_code, net::HTTP_OK);
+
+  data_decoder::DataDecoder::ParseJsonIsolated(
+      *body, base::BindOnce(&BoxGetFileFolderApiCallFlow::OnSuccessJsonParsed,
+                            weak_factory_.GetWeakPtr()));
+}
+
+void BoxGetFileFolderApiCallFlow::ProcessFailure(Response response) {
+  std::move(callback_).Run(response, std::string());
+}
+
+void BoxGetFileFolderApiCallFlow::OnSuccessJsonParsed(ParseResult result) {
+  std::string folder_id;
+
+  if (result.value.has_value())
+    folder_id = ExtractParentId(result.value.value());
+
+  std::move(callback_).Run(Response{!folder_id.empty(), net::HTTP_OK},
+                           folder_id);
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // FindUpstreamFolder
 ////////////////////////////////////////////////////////////////////////////////
 // BoxApiCallFlow interface.
diff --git a/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.h b/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.h
index c3c2d9d..105a51e 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.h
+++ b/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.h
@@ -53,6 +53,33 @@
   base::WeakPtrFactory<BoxApiCallFlow> weak_factory_{this};
 };
 
+// Helper for getting the folder of a file in Box.
+class BoxGetFileFolderApiCallFlow : public BoxApiCallFlow {
+ public:
+  // Additional callback arg is: folder_id for the downloads folder found in
+  // Box.
+  using TaskCallback = base::OnceCallback<void(Response, const std::string&)>;
+  explicit BoxGetFileFolderApiCallFlow(TaskCallback callback,
+                                       const std::string& file_id);
+  ~BoxGetFileFolderApiCallFlow() override;
+
+ protected:
+  // BoxApiCallFlow interface.
+  GURL CreateApiCallUrl() override;
+  void ProcessApiCallSuccess(const network::mojom::URLResponseHead* head,
+                             std::unique_ptr<std::string> body) override;
+  void ProcessFailure(Response response) override;
+
+ private:
+  // Callback for JsonParser that extracts folder id in ProcessApiCallSuccess().
+  void OnSuccessJsonParsed(ParseResult result);
+
+  // Callback from the uploader to report success, http_code, folder_id.
+  TaskCallback callback_;
+  const std::string file_id_;
+  base::WeakPtrFactory<BoxGetFileFolderApiCallFlow> weak_factory_{this};
+};
+
 // Helper for finding the downloads folder in Box.
 class BoxFindUpstreamFolderApiCallFlow : public BoxApiCallFlow {
  public:
diff --git a/chrome/browser/enterprise/connectors/file_system/box_api_call_flow_unittest.cc b/chrome/browser/enterprise/connectors/file_system/box_api_call_flow_unittest.cc
index 6f8d12b..ddfc167 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_api_call_flow_unittest.cc
+++ b/chrome/browser/enterprise/connectors/file_system/box_api_call_flow_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/json/json_writer.h"
 #include "base/run_loop.h"
+#include "base/strings/strcat.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/task_environment.h"
 #include "chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.h"
@@ -209,6 +210,73 @@
                          testing::ValuesIn(kTestSuccessResponses));
 
 ////////////////////////////////////////////////////////////////////////////////
+// GetFileFolder
+////////////////////////////////////////////////////////////////////////////////
+
+class BoxGetFileFolderApiCallFlowForTest : public BoxGetFileFolderApiCallFlow {
+ public:
+  using BoxGetFileFolderApiCallFlow::BoxGetFileFolderApiCallFlow;
+  using BoxGetFileFolderApiCallFlow::CreateApiCallUrl;
+  using BoxGetFileFolderApiCallFlow::ProcessApiCallFailure;
+  using BoxGetFileFolderApiCallFlow::ProcessApiCallSuccess;
+};
+
+class BoxGetFileFolderApiCallFlowTest
+    : public BoxFolderApiCallFlowTest<BoxGetFileFolderApiCallFlowForTest> {
+ protected:
+  void SetUp() override {
+    flow_ = std::make_unique<BoxGetFileFolderApiCallFlowForTest>(
+        base::BindOnce(&BoxGetFileFolderApiCallFlowTest::OnResponse,
+                       factory_.GetWeakPtr()),
+        kFileSystemBoxGetFileFolderFileId);
+  }
+
+  base::WeakPtrFactory<BoxGetFileFolderApiCallFlowTest> factory_{this};
+};
+
+TEST_F(BoxGetFileFolderApiCallFlowTest, CreateApiCallUrl) {
+  GURL path(base::StrCat({kFileSystemBoxGetFileFolderUrl, "/",
+                          kFileSystemBoxGetFileFolderFileId}));
+  ASSERT_EQ(flow_->CreateApiCallUrl(), path);
+}
+
+TEST_F(BoxGetFileFolderApiCallFlowTest, ProcessApiCallFailure) {
+  auto http_head = network::CreateURLResponseHead(net::HTTP_TOO_MANY_REQUESTS);
+  std::unique_ptr<std::string> body =
+      std::make_unique<std::string>(CreateFailureResponse(
+          net::HTTP_TOO_MANY_REQUESTS, "rate_limit_exceeded"));
+  flow_->ProcessApiCallFailure(net::OK, http_head.get(), std::move(body));
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_FALSE(processed_success_);
+  ASSERT_EQ(response_code_, net::HTTP_TOO_MANY_REQUESTS);
+  ASSERT_EQ(box_error_code_, "rate_limit_exceeded");
+  ASSERT_EQ(box_request_id_, kFileSystemBoxClientErrorResponseRequestId);
+  ASSERT_EQ(processed_folder_id_, "");
+}
+
+class BoxGetFileFolderApiCallFlowTest_ProcessApiCallSuccess
+    : public BoxGetFileFolderApiCallFlowTest {
+ public:
+  BoxGetFileFolderApiCallFlowTest_ProcessApiCallSuccess()
+      : head_(network::CreateURLResponseHead(net::HTTP_OK)) {}
+
+ protected:
+  network::mojom::URLResponseHeadPtr head_;
+};
+
+TEST_F(BoxGetFileFolderApiCallFlowTest_ProcessApiCallSuccess, Normal) {
+  auto http_head = network::CreateURLResponseHead(net::HTTP_OK);
+  flow_->ProcessApiCallSuccess(
+      http_head.get(),
+      std::make_unique<std::string>(kFileSystemBoxGetFileFolderResponseBody));
+  base::RunLoop().RunUntilIdle();
+  ASSERT_TRUE(processed_success_);
+  ASSERT_EQ(response_code_, net::HTTP_OK);
+  ASSERT_EQ(processed_folder_id_, kFileSystemBoxGetFileFolderResponseFolderId);
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // CreateUpstreamFolder
 ////////////////////////////////////////////////////////////////////////////////
 
diff --git a/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.cc b/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.cc
index 0c6c008..3b4bf57 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.cc
+++ b/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.cc
@@ -8,6 +8,7 @@
 #include "base/strings/stringprintf.h"
 
 namespace enterprise_connectors {
+const char kFileSystemBoxGetFileFolderUrl[] = "https://api.box.com/2.0/files";
 const char kFileSystemBoxFindFolderUrl[] =
     "https://api.box.com/2.0/search?type=folder&query=ChromeDownloads";
 const char kFileSystemBoxCreateFolderUrl[] = "https://api.box.com/2.0/folders";
@@ -35,6 +36,16 @@
                             box_error, http_code);
 }
 
+// For box Get File Folder
+const char kFileSystemBoxGetFileFolderFileId[] = "123";
+const char kFileSystemBoxGetFileFolderResponseBody[] = R"({
+    "id": 12345,
+    "parent": {
+      "id": 23456
+    }
+  })";
+const char kFileSystemBoxGetFileFolderResponseFolderId[] = "23456";
+
 // For Box Pre-Upload Steps/////////////////////////////////////////////////////
 
 const char kFileSystemBoxFindFolderResponseBody[] = R"({
diff --git a/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.h b/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.h
index c65b2fa..0570be0 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.h
+++ b/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.h
@@ -11,6 +11,7 @@
 namespace enterprise_connectors {
 
 // Expected url's for each of the Box mini classes for whole file upload.
+extern const char kFileSystemBoxGetFileFolderUrl[];
 extern const char kFileSystemBoxFindFolderUrl[];
 extern const char kFileSystemBoxCreateFolderUrl[];
 extern const char kFileSystemBoxPreflightCheckUrl[];
@@ -25,6 +26,12 @@
 
 // For Box Pre-Upload Steps/////////////////////////////////////////////////////
 
+// GetFileFolder: Expected file_id
+extern const char kFileSystemBoxGetFileFolderFileId[];
+// GetFileFolder: Expected response
+extern const char kFileSystemBoxGetFileFolderResponseBody[];
+// GetFileFolder: Expected folder_id
+extern const char kFileSystemBoxGetFileFolderResponseFolderId[];
 // Expected response from kFileSystemBoxFindFolderUrl.
 extern const char kFileSystemBoxFindFolderResponseBody[];
 // Expected folder id extracted from above.
diff --git a/chrome/browser/enterprise/connectors/file_system/signin_dialog_delegate.cc b/chrome/browser/enterprise/connectors/file_system/signin_dialog_delegate.cc
index e749c50..99b5cea 100644
--- a/chrome/browser/enterprise/connectors/file_system/signin_dialog_delegate.cc
+++ b/chrome/browser/enterprise/connectors/file_system/signin_dialog_delegate.cc
@@ -9,6 +9,9 @@
 
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/enterprise/connectors/file_system/box_api_call_endpoints.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "content/public/browser/browser_context.h"
@@ -17,16 +20,18 @@
 #include "content/public/browser/storage_partition.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "google_apis/gaia/oauth2_access_token_consumer.h"
-#include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
 #include "google_apis/gaia/oauth2_api_call_flow.h"
 #include "net/base/escape.h"
 #include "net/base/url_util.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/views/layout/fill_layout.h"
 #include "url/gurl.h"
 
+// TODO(https://crbug.com/1227477): move this class to chrome/browser/ui/views.
+
 namespace {
 
 // The OAuth token consumer name.
@@ -46,6 +51,31 @@
   return url.host() == "google.com" && url.path() == "/generate_204";
 }
 
+gfx::NativeWindow FindMostRelevantContextWindow(
+    const content::WebContents* web_contents) {
+  // Can't just use web_contents->GetNativeView(): it results in a dialog that
+  // disappears upon browser going out of focus and cannot be re-activated or
+  // closed by user.
+  auto* browser =
+      web_contents ? chrome::FindBrowserWithWebContents(web_contents) : nullptr;
+
+  // Back up methods are needed to find a window to attach the dialog to,
+  // because the |web_contents| from |download_item| is stored as a mapping
+  // inside of it and is not guaranteed to always exist or be valid. Example: if
+  // the original window got closed when download was still in progress; or if
+  // we need to resume file upload upon browser restart.
+  if (!browser) {
+    LOG(ERROR) << "Can't find window from download item; using active window";
+    browser = chrome::FindBrowserWithActiveWindow();
+  }
+  if (!browser) {
+    LOG(ERROR) << "Can't find active window; using last active window";
+    browser = chrome::FindLastActive();
+  }
+  DCHECK(browser);
+  return browser->window()->GetNativeWindow();
+}
+
 }  // namespace
 
 namespace enterprise_connectors {
@@ -60,7 +90,8 @@
       web_view_(std::make_unique<views::WebView>(browser_context)),
       callback_(std::move(callback)) {
   SetHasWindowSizeControls(true);
-  SetTitle(IDS_PROFILES_GAIA_SIGNIN_TITLE);
+  SetTitle(l10n_util::GetStringFUTF16(
+      IDS_FILE_SYSTEM_CONNECTOR_SIGNIN_DIALOG_TITLE, GetProviderName()));
   SetButtons(ui::DIALOG_BUTTON_NONE);
   set_use_custom_frame(false);
 
@@ -103,16 +134,19 @@
     const FileSystemSettings& settings,
     AuthorizationCompletedCallback callback) {
   content::BrowserContext* browser_context = web_contents->GetBrowserContext();
-  gfx::NativeView parent = web_contents->GetNativeView();
+  gfx::NativeWindow context = FindMostRelevantContextWindow(web_contents);
 
-  FileSystemSigninDialogDelegate* delegate = new FileSystemSigninDialogDelegate(
-      browser_context, settings, std::move(callback));
-  // Object will be deleted internally by widget via DeleteDelegate().
-  // TODO(https://crbug.com/1160012): use std::unique_ptr instead?
+  std::unique_ptr<FileSystemSigninDialogDelegate> delegate =
+      std::make_unique<FileSystemSigninDialogDelegate>(
+          browser_context, settings, std::move(callback));
 
-  views::DialogDelegate::CreateDialogWidget(delegate, nullptr, parent);
-  delegate->GetWidget()->Show();
-  // This only returns when the dialog is closed.
+  // We want a dialog whose lifetime is independent from that of |web_contents|,
+  // therefore using FindMostRelevantContextWindow() as context, instead of
+  // using web_contents->GetNativeView() as parent. This gives us a new
+  // top-level window.
+  auto* widget = views::DialogDelegate::CreateDialogWidget(
+      std::move(delegate), context, /* parent = */ nullptr);
+  widget->Show();
 }
 
 web_modal::WebContentsModalDialogHost*
@@ -220,6 +254,11 @@
   return std::string();
 }
 
+std::u16string FileSystemSigninDialogDelegate::GetProviderName() const {
+  DCHECK_EQ(settings_.service_provider, kBoxProviderName);
+  return l10n_util::GetStringUTF16(IDS_FILE_SYSTEM_CONNECTOR_BOX);
+}
+
 BEGIN_METADATA(FileSystemSigninDialogDelegate, views::DialogDelegateView)
 END_METADATA
 
diff --git a/chrome/browser/enterprise/connectors/file_system/signin_dialog_delegate.h b/chrome/browser/enterprise/connectors/file_system/signin_dialog_delegate.h
index b9e35bd..caf6bdd 100644
--- a/chrome/browser/enterprise/connectors/file_system/signin_dialog_delegate.h
+++ b/chrome/browser/enterprise/connectors/file_system/signin_dialog_delegate.h
@@ -43,11 +43,11 @@
                          const FileSystemSettings& settings,
                          AuthorizationCompletedCallback callback);
 
- private:
   FileSystemSigninDialogDelegate(content::BrowserContext* browser_context,
                                  const FileSystemSettings& settings,
                                  AuthorizationCompletedCallback callback);
 
+ private:
   // ChromeWebModalDialogManagerDelegate:
   web_modal::WebContentsModalDialogHost* GetWebContentsModalDialogHost()
       override;
@@ -78,6 +78,9 @@
   // May return the empty string if there are none.
   std::string GetProviderSpecificUrlParameters();
 
+  // Return display name for the service provider.
+  std::u16string GetProviderName() const;
+
   const FileSystemSettings settings_;
   std::unique_ptr<views::WebView> web_view_;
   std::unique_ptr<OAuth2AccessTokenFetcherImpl> token_fetcher_;
diff --git a/chrome/browser/enterprise/signals/context_info_fetcher.cc b/chrome/browser/enterprise/signals/context_info_fetcher.cc
index 29aca41..b3bd98a30 100644
--- a/chrome/browser/enterprise/signals/context_info_fetcher.cc
+++ b/chrome/browser/enterprise/signals/context_info_fetcher.cc
@@ -16,12 +16,30 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
 #include "components/component_updater/pref_names.h"
+#include "components/policy/content/policy_blocklist_service.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/site_isolation_policy.h"
 #include "device_management_backend.pb.h"
 
 namespace enterprise_signals {
 
+namespace {
+
+bool IsURLBlocked(const GURL& url, content::BrowserContext* browser_context_) {
+  PolicyBlocklistService* service =
+      PolicyBlocklistFactory::GetForBrowserContext(browser_context_);
+
+  if (!service)
+    return false;
+
+  policy::URLBlocklist::URLBlocklistState state =
+      service->GetURLBlocklistState(url);
+
+  return state == policy::URLBlocklist::URLBlocklistState::URL_IN_BLOCKLIST;
+}
+
+}  // namespace
+
 ContextInfo::ContextInfo() = default;
 ContextInfo::ContextInfo(ContextInfo&&) = default;
 ContextInfo::~ContextInfo() = default;
@@ -67,6 +85,7 @@
   info.password_protection_warning_trigger =
       GetPasswordProtectionWarningTrigger();
   info.chrome_cleanup_enabled = GetChromeCleanupEnabled();
+  info.chrome_remote_desktop_app_blocked = GetChromeRemoteDesktopAppBlocked();
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), std::move(info)));
 }
@@ -133,6 +152,7 @@
       profile->GetPrefs()->GetInteger(
           prefs::kPasswordProtectionWarningTrigger));
 }
+
 absl::optional<bool> ContextInfoFetcher::GetChromeCleanupEnabled() {
 #if defined(OS_WIN)
   return g_browser_process->local_state()->GetBoolean(
@@ -142,4 +162,11 @@
 #endif
 }
 
+bool ContextInfoFetcher::GetChromeRemoteDesktopAppBlocked() {
+  return IsURLBlocked(GURL("https://remotedesktop.google.com"),
+                      browser_context_) ||
+         IsURLBlocked(GURL("https://remotedesktop.corp.google.com"),
+                      browser_context_);
+}
+
 }  // namespace enterprise_signals
diff --git a/chrome/browser/enterprise/signals/context_info_fetcher.h b/chrome/browser/enterprise/signals/context_info_fetcher.h
index 823e4c7..18ec5b3 100644
--- a/chrome/browser/enterprise/signals/context_info_fetcher.h
+++ b/chrome/browser/enterprise/signals/context_info_fetcher.h
@@ -43,6 +43,7 @@
   absl::optional<safe_browsing::PasswordProtectionTrigger>
       password_protection_warning_trigger;
   absl::optional<bool> chrome_cleanup_enabled;
+  bool chrome_remote_desktop_app_blocked;
 };
 
 // Interface used by the chrome.enterprise.reportingPrivate.getContextInfo()
@@ -95,6 +96,8 @@
 
   absl::optional<bool> GetChromeCleanupEnabled();
 
+  bool GetChromeRemoteDesktopAppBlocked();
+
   content::BrowserContext* browser_context_;
 
   // |connectors_service| is used to obtain the value of each Connector policy.
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
index 79bc7cd..cd80bcd 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
@@ -64,6 +64,8 @@
       signals.chrome_cleanup_enabled.has_value()
           ? std::make_unique<bool>(signals.chrome_cleanup_enabled.value())
           : nullptr;
+  info.chrome_remote_desktop_app_blocked =
+      signals.chrome_remote_desktop_app_blocked;
   switch (signals.realtime_url_check_mode) {
     case safe_browsing::REAL_TIME_CHECK_DISABLED:
       info.realtime_url_check_mode = extensions::api::
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc
index abad01b7..2173aeb 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc
@@ -252,10 +252,10 @@
 IN_PROC_BROWSER_TEST_F(EnterpriseReportingPrivateApiTest, GetContextInfo) {
 #if defined(OS_WIN)
   constexpr char kChromeCleanupEnabledType[] = "boolean";
-  constexpr char kCount[] = "13";
+  constexpr char kCount[] = "14";
 #else
   constexpr char kChromeCleanupEnabledType[] = "undefined";
-  constexpr char kCount[] = "12";
+  constexpr char kCount[] = "13";
 #endif
 
   constexpr char kTest[] = R"(
@@ -280,6 +280,8 @@
       chrome.test.assertEq
         (typeof info.passwordProtectionWarningTrigger, 'string');
       chrome.test.assertEq(typeof info.chromeCleanupEnabled, '%s');
+      chrome.test.assertEq
+        (typeof info.chromeRemoteDesktopAppBlocked, 'boolean');
 
       chrome.test.notifyPass();
     });)";
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_browsertest.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_browsertest.cc
index 87729b7..61268f47 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_browsertest.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_browsertest.cc
@@ -297,6 +297,7 @@
 #else
   EXPECT_EQ(nullptr, info.chrome_cleanup_enabled.get());
 #endif
+  EXPECT_FALSE(info.chrome_remote_desktop_app_blocked);
 }
 
 IN_PROC_BROWSER_TEST_F(EnterpriseReportingPrivateGetContextInfoBaseBrowserTest,
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
index d42d401..2e5b0473 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
@@ -20,6 +20,7 @@
 #include "chrome/common/pref_names.h"
 #include "components/component_updater/pref_names.h"
 #include "components/enterprise/browser/controller/fake_browser_dm_token_storage.h"
+#include "components/policy/core/common/policy_pref_names.h"
 #include "components/policy/core/common/policy_types.h"
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/version_info/version_info.h"
@@ -392,6 +393,15 @@
     return false;
 #endif
   }
+
+  void ExpectDefaultChromeCleanupEnabled(
+      enterprise_reporting_private::ContextInfo& info) {
+#if defined(OS_WIN)
+    EXPECT_TRUE(*info.chrome_cleanup_enabled);
+#else
+    EXPECT_EQ(nullptr, info.chrome_cleanup_enabled.get());
+#endif
+  }
 };
 
 TEST_F(EnterpriseReportingPrivateGetContextInfoTest, NoSpecialContext) {
@@ -415,11 +425,8 @@
   EXPECT_EQ(
       enterprise_reporting_private::PASSWORD_PROTECTION_TRIGGER_POLICY_UNSET,
       info.password_protection_warning_trigger);
-#if defined(OS_WIN)
-  EXPECT_TRUE(*info.chrome_cleanup_enabled);
-#else
-  EXPECT_EQ(nullptr, info.chrome_cleanup_enabled.get());
-#endif
+  ExpectDefaultChromeCleanupEnabled(info);
+  EXPECT_FALSE(info.chrome_remote_desktop_app_blocked);
 }
 
 class EnterpriseReportingPrivateGetContextInfoSafeBrowsingTest
@@ -605,6 +612,89 @@
     testing::Bool());
 #endif  // defined(OS_WIN)
 
+class EnterpriseReportingPrivateGetContextInfoChromeRemoteDesktopAppBlockedTest
+    : public EnterpriseReportingPrivateGetContextInfoTest,
+      public testing::WithParamInterface<const char*> {
+ public:
+  void SetURLBlockedPolicy(const std::string& url) {
+    base::Value blockList(base::Value::Type::LIST);
+    blockList.Append(base::Value(url));
+
+    profile()->GetPrefs()->Set(policy::policy_prefs::kUrlBlocklist,
+                               std::move(blockList));
+  }
+  void SetURLAllowedPolicy(const std::string& url) {
+    base::Value allowList(base::Value::Type::LIST);
+    allowList.Append(base::Value(url));
+
+    profile()->GetPrefs()->Set(policy::policy_prefs::kUrlAllowlist,
+                               std::move(allowList));
+  }
+
+  void ExpectDefaultPolicies(enterprise_reporting_private::ContextInfo& info) {
+    EXPECT_TRUE(info.browser_affiliation_ids.empty());
+    EXPECT_TRUE(info.profile_affiliation_ids.empty());
+    EXPECT_TRUE(info.on_file_attached_providers.empty());
+    EXPECT_TRUE(info.on_file_downloaded_providers.empty());
+    EXPECT_TRUE(info.on_bulk_data_entry_providers.empty());
+    EXPECT_EQ(enterprise_reporting_private::REALTIME_URL_CHECK_MODE_DISABLED,
+              info.realtime_url_check_mode);
+    EXPECT_TRUE(info.on_security_event_providers.empty());
+    EXPECT_EQ(version_info::GetVersionNumber(), info.browser_version);
+    EXPECT_EQ(enterprise_reporting_private::SAFE_BROWSING_LEVEL_STANDARD,
+              info.safe_browsing_protection_level);
+    EXPECT_EQ(BuiltInDnsClientPlatformDefault(),
+              info.built_in_dns_client_enabled);
+    EXPECT_EQ(
+        enterprise_reporting_private::PASSWORD_PROTECTION_TRIGGER_POLICY_UNSET,
+        info.password_protection_warning_trigger);
+    ExpectDefaultChromeCleanupEnabled(info);
+  }
+};
+
+TEST_P(
+    EnterpriseReportingPrivateGetContextInfoChromeRemoteDesktopAppBlockedTest,
+    BlockedURL) {
+  SetURLBlockedPolicy(GetParam());
+
+  enterprise_reporting_private::ContextInfo info = GetContextInfo();
+
+  ExpectDefaultPolicies(info);
+  EXPECT_TRUE(info.chrome_remote_desktop_app_blocked);
+}
+
+TEST_P(
+    EnterpriseReportingPrivateGetContextInfoChromeRemoteDesktopAppBlockedTest,
+    AllowedURL) {
+  SetURLAllowedPolicy(GetParam());
+
+  enterprise_reporting_private::ContextInfo info = GetContextInfo();
+
+  ExpectDefaultPolicies(info);
+  EXPECT_FALSE(info.chrome_remote_desktop_app_blocked);
+}
+
+TEST_P(
+    EnterpriseReportingPrivateGetContextInfoChromeRemoteDesktopAppBlockedTest,
+    BlockedAndAllowedURL) {
+  SetURLBlockedPolicy(GetParam());
+  SetURLAllowedPolicy(GetParam());
+
+  enterprise_reporting_private::ContextInfo info = GetContextInfo();
+
+  ExpectDefaultPolicies(info);
+  EXPECT_FALSE(info.chrome_remote_desktop_app_blocked);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    EnterpriseReportingPrivateGetContextInfoChromeRemoteDesktopAppBlockedTest,
+    testing::Values("https://remotedesktop.google.com",
+                    "https://remotedesktop.corp.google.com",
+                    "corp.google.com",
+                    "google.com",
+                    "https://*"));
+
 class EnterpriseReportingPrivateGetContextInfoRealTimeURLCheckTest
     : public EnterpriseReportingPrivateGetContextInfoTest,
       public testing::WithParamInterface<bool> {
diff --git a/chrome/browser/extensions/api/scripting/scripting_api.cc b/chrome/browser/extensions/api/scripting/scripting_api.cc
index d197f53..01e5acc 100644
--- a/chrome/browser/extensions/api/scripting/scripting_api.cc
+++ b/chrome/browser/extensions/api/scripting/scripting_api.cc
@@ -40,6 +40,8 @@
 namespace {
 
 constexpr char kCouldNotLoadFileError[] = "Could not load file: '*'.";
+constexpr char kDuplicateFileSpecifiedError[] =
+    "Duplicate file specified: '*'.";
 constexpr char kExactlyOneOfCssAndFilesError[] =
     "Exactly one of 'css' and 'files' must be specified.";
 
@@ -76,26 +78,164 @@
   return css_origin;
 }
 
-// Checks `files` and populates `resource_out` with the appropriate extension
-// resource. Returns true on success; on failure, populates `error_out`.
-bool GetFileResource(const std::vector<std::string>& files,
-                     const Extension& extension,
-                     ExtensionResource* resource_out,
-                     std::string* error_out) {
-  if (files.size() != 1) {
-    constexpr char kExactlyOneFileError[] =
-        "Exactly one file must be specified.";
-    *error_out = kExactlyOneFileError;
-    return false;
+std::string InjectionKeyForCode(const mojom::HostID& host_id,
+                                const std::string& code) {
+  return ScriptExecutor::GenerateInjectionKey(host_id, /*script_url=*/GURL(),
+                                              code);
+}
+
+std::string InjectionKeyForFile(const mojom::HostID& host_id,
+                                const GURL& resource_url) {
+  return ScriptExecutor::GenerateInjectionKey(host_id, resource_url,
+                                              /*code=*/std::string());
+}
+
+// Constructs an array of file sources from the read file `data`.
+std::vector<InjectedFileSource> ConstructFileSources(
+    std::vector<std::unique_ptr<std::string>> data,
+    std::vector<std::string> file_names) {
+  // Note: CHECK (and not DCHECK) because if it fails, we have an out-of-bounds
+  // access.
+  CHECK_EQ(data.size(), file_names.size());
+  const size_t num_sources = data.size();
+  std::vector<InjectedFileSource> sources;
+  sources.reserve(num_sources);
+  for (size_t i = 0; i < num_sources; ++i)
+    sources.emplace_back(std::move(file_names[i]), std::move(data[i]));
+
+  return sources;
+}
+
+std::vector<mojom::JSSourcePtr> FileSourcesToJSSources(
+    const Extension& extension,
+    std::vector<InjectedFileSource> file_sources) {
+  std::vector<mojom::JSSourcePtr> js_sources;
+  js_sources.reserve(file_sources.size());
+  for (auto& file_source : file_sources) {
+    js_sources.push_back(
+        mojom::JSSource::New(std::move(*file_source.data),
+                             extension.GetResourceURL(file_source.file_name)));
   }
-  ExtensionResource resource = extension.GetResource(files[0]);
-  if (resource.extension_root().empty() || resource.relative_path().empty()) {
-    *error_out =
-        ErrorUtils::FormatErrorMessage(kCouldNotLoadFileError, files[0]);
+
+  return js_sources;
+}
+
+std::vector<mojom::CSSSourcePtr> FileSourcesToCSSSources(
+    const Extension& extension,
+    std::vector<InjectedFileSource> file_sources) {
+  mojom::HostID host_id(mojom::HostID::HostType::kExtensions, extension.id());
+
+  std::vector<mojom::CSSSourcePtr> css_sources;
+  css_sources.reserve(file_sources.size());
+  for (auto& file_source : file_sources) {
+    css_sources.push_back(mojom::CSSSource::New(
+        std::move(*file_source.data),
+        InjectionKeyForFile(host_id,
+                            extension.GetResourceURL(file_source.file_name))));
+  }
+
+  return css_sources;
+}
+
+// Checks `files` and populates `resources_out` with the appropriate extension
+// resource. Returns true on success; on failure, populates `error_out`.
+bool GetFileResources(const std::vector<std::string>& files,
+                      const Extension& extension,
+                      std::vector<ExtensionResource>* resources_out,
+                      std::string* error_out) {
+  if (files.empty()) {
+    static constexpr char kAtLeastOneFileError[] =
+        "At least one file must be specified.";
+    *error_out = kAtLeastOneFileError;
     return false;
   }
 
-  *resource_out = std::move(resource);
+  std::vector<ExtensionResource> resources;
+  for (const auto& file : files) {
+    ExtensionResource resource = extension.GetResource(file);
+    if (resource.extension_root().empty() || resource.relative_path().empty()) {
+      *error_out = ErrorUtils::FormatErrorMessage(kCouldNotLoadFileError, file);
+      return false;
+    }
+
+    // ExtensionResource doesn't implement an operator==.
+    auto existing = base::ranges::find_if(
+        resources, [&resource](const ExtensionResource& other) {
+          return resource.relative_path() == other.relative_path();
+        });
+
+    if (existing != resources.end()) {
+      // Disallow duplicates. Note that we could allow this, if we wanted (and
+      // there *might* be reason to with JS injection, to perform an operation
+      // twice?). However, this matches content script behavior, and injecting
+      // twice can be done by chaining calls to executeScript() / insertCSS().
+      // This isn't a robust check, and could probably be circumvented by
+      // passing two paths that look different but are the same - but in that
+      // case, we just try to load and inject the script twice, which is
+      // inefficient, but safe.
+      *error_out =
+          ErrorUtils::FormatErrorMessage(kDuplicateFileSpecifiedError, file);
+      return false;
+    }
+
+    resources.push_back(std::move(resource));
+  }
+
+  resources_out->swap(resources);
+  return true;
+}
+
+using ResourcesLoadedCallback =
+    base::OnceCallback<void(std::vector<InjectedFileSource>,
+                            absl::optional<std::string>)>;
+
+// Checks the loaded content of extension resources. Invokes `callback` with
+// the constructed file sources on success or with an error on failure.
+void CheckLoadedResources(std::vector<std::string> file_names,
+                          ResourcesLoadedCallback callback,
+                          std::vector<std::unique_ptr<std::string>> file_data,
+                          absl::optional<std::string> load_error) {
+  if (load_error) {
+    std::move(callback).Run({}, std::move(load_error));
+    return;
+  }
+
+  std::vector<InjectedFileSource> file_sources =
+      ConstructFileSources(std::move(file_data), std::move(file_names));
+
+  for (const auto& source : file_sources) {
+    DCHECK(source.data);
+    // TODO(devlin): What necessitates this encoding requirement? Is it needed
+    // for blink injection?
+    if (!base::IsStringUTF8(*source.data)) {
+      static constexpr char kBadFileEncodingError[] =
+          "Could not load file '*'. It isn't UTF-8 encoded.";
+      std::string error = ErrorUtils::FormatErrorMessage(kBadFileEncodingError,
+                                                         source.file_name);
+      std::move(callback).Run({}, std::move(error));
+      return;
+    }
+  }
+
+  std::move(callback).Run(std::move(file_sources), absl::nullopt);
+}
+
+// Checks the specified `files` for validity, and attempts to load and localize
+// them, invoking `callback` with the result. Returns true on success; on
+// failure, populates `error`.
+bool CheckAndLoadFiles(std::vector<std::string> files,
+                       const Extension& extension,
+                       bool requires_localization,
+                       ResourcesLoadedCallback callback,
+                       std::string* error) {
+  std::vector<ExtensionResource> resources;
+  if (!GetFileResources(files, extension, &resources, error))
+    return false;
+
+  LoadAndLocalizeResources(
+      extension, resources, requires_localization,
+      base::BindOnce(&CheckLoadedResources, std::move(files),
+                     std::move(callback)));
   return true;
 }
 
@@ -197,40 +337,6 @@
   return true;
 }
 
-// Returns true if the loaded resource is valid for injection.
-bool CheckLoadedResource(std::string* data,
-                         const std::string& file_name,
-                         std::string* error) {
-  DCHECK(data);
-  // TODO(devlin): What necessitates this encoding requirement? Is it needed for
-  // blink injection?
-  if (!base::IsStringUTF8(*data)) {
-    constexpr char kBadFileEncodingError[] =
-        "Could not load file '*'. It isn't UTF-8 encoded.";
-    *error = ErrorUtils::FormatErrorMessage(kBadFileEncodingError, file_name);
-    return false;
-  }
-
-  return true;
-}
-
-// Checks the specified `files` for validity, and attempts to load and localize
-// them, invoking `callback` with the result. Returns true on success; on
-// failure, populates `error`.
-bool CheckAndLoadFiles(const std::vector<std::string>& files,
-                       const Extension& extension,
-                       bool requires_localization,
-                       LoadAndLocalizeResourcesCallback callback,
-                       std::string* error) {
-  ExtensionResource resource;
-  if (!GetFileResource(files, extension, &resource, error))
-    return false;
-
-  LoadAndLocalizeResources(extension, {std::move(resource)},
-                           requires_localization, std::move(callback));
-  return true;
-}
-
 std::unique_ptr<UserScript> ParseUserScript(
     const Extension& extension,
     const api::scripting::RegisteredContentScript& content_script,
@@ -284,6 +390,12 @@
 
 }  // namespace
 
+InjectedFileSource::InjectedFileSource(std::string file_name,
+                                       std::unique_ptr<std::string> data)
+    : file_name(std::move(file_name)), data(std::move(data)) {}
+InjectedFileSource::InjectedFileSource(InjectedFileSource&&) = default;
+InjectedFileSource::~InjectedFileSource() = default;
+
 ScriptingExecuteScriptFunction::ScriptingExecuteScriptFunction() = default;
 ScriptingExecuteScriptFunction::~ScriptingExecuteScriptFunction() = default;
 
@@ -318,8 +430,8 @@
     constexpr bool kRequiresLocalization = false;
     std::string error;
     if (!CheckAndLoadFiles(
-            *injection_.files, *extension(), kRequiresLocalization,
-            base::BindOnce(&ScriptingExecuteScriptFunction::DidLoadResource,
+            std::move(*injection_.files), *extension(), kRequiresLocalization,
+            base::BindOnce(&ScriptingExecuteScriptFunction::DidLoadResources,
                            this),
             &error)) {
       return RespondNow(Error(std::move(error)));
@@ -349,41 +461,36 @@
   std::string code_to_execute = base::StringPrintf(
       "(%s)(%s)", injection_.func->c_str(), args_expression.c_str());
 
+  std::vector<mojom::JSSourcePtr> sources;
+  sources.push_back(mojom::JSSource::New(std::move(code_to_execute), GURL()));
+
   std::string error;
-  if (!Execute(std::move(code_to_execute), /*script_src=*/GURL(), &error))
+  if (!Execute(std::move(sources), &error))
     return RespondNow(Error(std::move(error)));
 
   return RespondLater();
 }
 
-void ScriptingExecuteScriptFunction::DidLoadResource(
-    std::vector<std::unique_ptr<std::string>> data,
+void ScriptingExecuteScriptFunction::DidLoadResources(
+    std::vector<InjectedFileSource> file_sources,
     absl::optional<std::string> load_error) {
-  DCHECK(injection_.files);
-  DCHECK_EQ(1u, injection_.files->size());
-
   if (load_error) {
     Respond(Error(std::move(*load_error)));
     return;
   }
 
-  // TODO(devlin): Remove this DCHECK when multiple files are supported.
-  DCHECK_EQ(1u, data.size());
-  auto file_data = std::move(data.front());
-  std::string error;
-  if (!CheckLoadedResource(file_data.get(), injection_.files->at(0), &error)) {
-    Respond(Error(std::move(error)));
-    return;
-  }
+  DCHECK(!file_sources.empty());
 
-  GURL script_url = extension()->GetResourceURL(injection_.files->at(0));
-  if (!Execute(std::move(*file_data), std::move(script_url), &error))
+  std::string error;
+  if (!Execute(FileSourcesToJSSources(*extension(), std::move(file_sources)),
+               &error)) {
     Respond(Error(std::move(error)));
+  }
 }
 
-bool ScriptingExecuteScriptFunction::Execute(std::string code_to_execute,
-                                             GURL script_url,
-                                             std::string* error) {
+bool ScriptingExecuteScriptFunction::Execute(
+    std::vector<mojom::JSSourcePtr> sources,
+    std::string* error) {
   ScriptExecutor* script_executor = nullptr;
   ScriptExecutor::FrameScope frame_scope = ScriptExecutor::SPECIFIED_FRAMES;
   std::set<int> frame_ids;
@@ -393,9 +500,6 @@
     return false;
   }
 
-  std::vector<mojom::JSSourcePtr> sources;
-  sources.push_back(
-      mojom::JSSource::New(std::move(code_to_execute), std::move(script_url)));
   script_executor->ExecuteScript(
       mojom::HostID(mojom::HostID::HostType::kExtensions, extension()->id()),
       mojom::CodeInjection::NewJs(mojom::JSInjection::New(std::move(sources),
@@ -463,8 +567,8 @@
     constexpr bool kRequiresLocalization = true;
     std::string error;
     if (!CheckAndLoadFiles(
-            *injection_.files, *extension(), kRequiresLocalization,
-            base::BindOnce(&ScriptingInsertCSSFunction::DidLoadResource, this),
+            std::move(*injection_.files), *extension(), kRequiresLocalization,
+            base::BindOnce(&ScriptingInsertCSSFunction::DidLoadResources, this),
             &error)) {
       return RespondNow(Error(std::move(error)));
     }
@@ -473,42 +577,42 @@
 
   DCHECK(injection_.css);
 
+  mojom::HostID host_id(mojom::HostID::HostType::kExtensions,
+                        extension()->id());
+
+  std::vector<mojom::CSSSourcePtr> sources;
+  sources.push_back(
+      mojom::CSSSource::New(std::move(*injection_.css),
+                            InjectionKeyForCode(host_id, *injection_.css)));
+
   std::string error;
-  if (!Execute(std::move(*injection_.css), /*script_url=*/GURL(), &error)) {
+  if (!Execute(std::move(sources), &error)) {
     return RespondNow(Error(std::move(error)));
   }
 
   return RespondLater();
 }
 
-void ScriptingInsertCSSFunction::DidLoadResource(
-    std::vector<std::unique_ptr<std::string>> data,
+void ScriptingInsertCSSFunction::DidLoadResources(
+    std::vector<InjectedFileSource> file_sources,
     absl::optional<std::string> load_error) {
-  DCHECK(injection_.files);
-  DCHECK_EQ(1u, injection_.files->size());
-
   if (load_error) {
     Respond(Error(std::move(*load_error)));
     return;
   }
 
-  // TODO(devlin): Remove this DCHECK when multiple files are supported.
-  DCHECK_EQ(1u, data.size());
-  std::string error;
-  auto& file_data = data.front();
-  if (!CheckLoadedResource(file_data.get(), injection_.files->at(0), &error)) {
-    Respond(Error(std::move(error)));
-    return;
-  }
+  DCHECK(!file_sources.empty());
+  std::vector<mojom::CSSSourcePtr> sources =
+      FileSourcesToCSSSources(*extension(), std::move(file_sources));
 
-  GURL script_url = extension()->GetResourceURL(injection_.files->at(0));
-  if (!Execute(std::move(*file_data), std::move(script_url), &error))
+  std::string error;
+  if (!Execute(std::move(sources), &error))
     Respond(Error(std::move(error)));
 }
 
-bool ScriptingInsertCSSFunction::Execute(std::string code_to_execute,
-                                         GURL script_url,
-                                         std::string* error) {
+bool ScriptingInsertCSSFunction::Execute(
+    std::vector<mojom::CSSSourcePtr> sources,
+    std::string* error) {
   ScriptExecutor* script_executor = nullptr;
   ScriptExecutor::FrameScope frame_scope = ScriptExecutor::SPECIFIED_FRAMES;
   std::set<int> frame_ids;
@@ -519,16 +623,8 @@
   }
   DCHECK(script_executor);
 
-  mojom::HostID host_id(mojom::HostID::HostType::kExtensions,
-                        extension()->id());
-  std::string injection_key = ScriptExecutor::GenerateInjectionKey(
-      host_id, script_url, code_to_execute);
-
-  std::vector<mojom::CSSSourcePtr> sources;
-  sources.push_back(mojom::CSSSource::New(std::move(code_to_execute),
-                                          std::move(injection_key)));
   script_executor->ExecuteScript(
-      std::move(host_id),
+      mojom::HostID(mojom::HostID::HostType::kExtensions, extension()->id()),
       mojom::CodeInjection::NewCss(mojom::CSSInjection::New(
           std::move(sources), ConvertStyleOriginToCSSOrigin(injection_.origin),
           mojom::CSSInjection::Operation::kAdd)),
@@ -567,25 +663,10 @@
     return RespondNow(Error(kExactlyOneOfCssAndFilesError));
   }
 
-  GURL script_url;
-  std::string error;
-  std::string code;
-  if (injection.files) {
-    // Note: Since we're just removing the CSS, we don't actually need to load
-    // the file here. It's okay for `code` to be empty in this case.
-    ExtensionResource resource;
-    if (!GetFileResource(*injection.files, *extension(), &resource, &error))
-      return RespondNow(Error(std::move(error)));
-
-    script_url = extension()->GetResourceURL(injection.files->at(0));
-  } else {
-    DCHECK(injection.css);
-    code = std::move(*injection.css);
-  }
-
   ScriptExecutor* script_executor = nullptr;
   ScriptExecutor::FrameScope frame_scope = ScriptExecutor::SPECIFIED_FRAMES;
   std::set<int> frame_ids;
+  std::string error;
   if (!CanAccessTarget(*extension()->permissions_data(), injection.target,
                        browser_context(), include_incognito_information(),
                        &script_executor, &frame_scope, &frame_ids, &error)) {
@@ -593,16 +674,32 @@
   }
   DCHECK(script_executor);
 
-  DCHECK(code.empty() || !script_url.is_valid());
-
   mojom::HostID host_id(mojom::HostID::HostType::kExtensions,
                         extension()->id());
-  std::string injection_key =
-      ScriptExecutor::GenerateInjectionKey(host_id, script_url, code);
-
   std::vector<mojom::CSSSourcePtr> sources;
-  sources.push_back(
-      mojom::CSSSource::New(std::move(code), std::move(injection_key)));
+
+  if (injection.files) {
+    std::vector<ExtensionResource> resources;
+    if (!GetFileResources(*injection.files, *extension(), &resources, &error))
+      return RespondNow(Error(std::move(error)));
+
+    // Note: Since we're just removing the CSS, we don't actually need to load
+    // the file here. It's okay for `code` to be empty in this case.
+    const std::string empty_code;
+    sources.reserve(injection.files->size());
+
+    for (const auto& file : *injection.files) {
+      sources.push_back(mojom::CSSSource::New(
+          empty_code,
+          InjectionKeyForFile(host_id, extension()->GetResourceURL(file))));
+    }
+  } else {
+    DCHECK(injection.css);
+    sources.push_back(
+        mojom::CSSSource::New(std::move(*injection.css),
+                              InjectionKeyForCode(host_id, *injection.css)));
+  }
+
   script_executor->ExecuteScript(
       std::move(host_id),
       mojom::CodeInjection::NewCss(mojom::CSSInjection::New(
diff --git a/chrome/browser/extensions/api/scripting/scripting_api.h b/chrome/browser/extensions/api/scripting/scripting_api.h
index 8e7fc14..bf26a27 100644
--- a/chrome/browser/extensions/api/scripting/scripting_api.h
+++ b/chrome/browser/extensions/api/scripting/scripting_api.h
@@ -13,11 +13,23 @@
 #include "chrome/common/extensions/api/scripting.h"
 #include "extensions/browser/extension_function.h"
 #include "extensions/browser/script_executor.h"
+#include "extensions/common/mojom/code_injection.mojom.h"
 #include "extensions/common/user_script.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace extensions {
 
+// A simple helper struct to represent a read file (either CSS or JS) to be
+// injected.
+struct InjectedFileSource {
+  InjectedFileSource(std::string file_name, std::unique_ptr<std::string> data);
+  InjectedFileSource(InjectedFileSource&&);
+  ~InjectedFileSource();
+
+  std::string file_name;
+  std::unique_ptr<std::string> data;
+};
+
 class ScriptingExecuteScriptFunction : public ExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("scripting.executeScript", SCRIPTING_EXECUTESCRIPT)
@@ -34,15 +46,13 @@
  private:
   ~ScriptingExecuteScriptFunction() override;
 
-  // Called when the resource file to be injected has been loaded.
-  void DidLoadResource(std::vector<std::unique_ptr<std::string>> data,
-                       absl::optional<std::string> load_error);
+  // Called when the resource files to be injected has been loaded.
+  void DidLoadResources(std::vector<InjectedFileSource> file_sources,
+                        absl::optional<std::string> load_error);
 
-  // Triggers the execution of `code_to_execute` in the appropriate context.
+  // Triggers the execution of `sources` in the appropriate context.
   // Returns true on success; on failure, populates `error`.
-  bool Execute(std::string code_to_execute,
-               GURL script_url,
-               std::string* error);
+  bool Execute(std::vector<mojom::JSSourcePtr> sources, std::string* error);
 
   // Invoked when script execution is complete.
   void OnScriptExecuted(std::vector<ScriptExecutor::FrameResult> frame_results);
@@ -65,15 +75,13 @@
  private:
   ~ScriptingInsertCSSFunction() override;
 
-  // Called when the resource file to be injected has been loaded.
-  void DidLoadResource(std::vector<std::unique_ptr<std::string>> data,
-                       absl::optional<std::string> load_error);
+  // Called when the resource files to be injected has been loaded.
+  void DidLoadResources(std::vector<InjectedFileSource> file_sources,
+                        absl::optional<std::string> load_error);
 
-  // Triggers the execution of `code_to_execute` in the appropriate context.
+  // Triggers the execution of `sources` in the appropriate context.
   // Returns true on success; on failure, populates `error`.
-  bool Execute(std::string code_to_execute,
-               GURL script_url,
-               std::string* error);
+  bool Execute(std::vector<mojom::CSSSourcePtr> sources, std::string* error);
 
   // Called when the CSS insertion is complete.
   void OnCSSInserted(std::vector<ScriptExecutor::FrameResult> results);
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index 909b49e..94cca61 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -44,12 +44,12 @@
 #include "chrome/browser/net/profile_network_context_service.h"
 #include "chrome/browser/net/profile_network_context_service_factory.h"
 #include "chrome/browser/net/system_network_context_manager.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_service.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_destroyer.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_loader.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_service.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_service_factory.h"
 #include "chrome/browser/search/search.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/ui/browser.h"
@@ -1985,18 +1985,18 @@
 }
 
 // Test fixture testing that requests made for the OneGoogleBar on behalf of
-// the local NTP can't be intercepted by extensions.
-class LocalNTPInterceptionWebRequestAPITest
+// the WebUI NTP can't be intercepted by extensions.
+class WebUiNtpInterceptionWebRequestAPITest
     : public ExtensionApiTest,
       public OneGoogleBarServiceObserver {
  public:
-  LocalNTPInterceptionWebRequestAPITest()
+  WebUiNtpInterceptionWebRequestAPITest()
       : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
 
   // ExtensionApiTest override:
   void SetUp() override {
     https_test_server_.RegisterRequestMonitor(base::BindRepeating(
-        &LocalNTPInterceptionWebRequestAPITest::MonitorRequest,
+        &WebUiNtpInterceptionWebRequestAPITest::MonitorRequest,
         base::Unretained(this)));
     ASSERT_TRUE(https_test_server_.InitializeAndListen());
     ExtensionApiTest::SetUp();
@@ -2063,10 +2063,10 @@
 
   base::Lock lock_;
 
-  DISALLOW_COPY_AND_ASSIGN(LocalNTPInterceptionWebRequestAPITest);
+  DISALLOW_COPY_AND_ASSIGN(WebUiNtpInterceptionWebRequestAPITest);
 };
 
-IN_PROC_BROWSER_TEST_F(LocalNTPInterceptionWebRequestAPITest,
+IN_PROC_BROWSER_TEST_F(WebUiNtpInterceptionWebRequestAPITest,
                        OneGoogleBarRequestsHidden) {
   // Loads an extension which tries to intercept requests to the OneGoogleBar.
   ExtensionTestMessageListener listener("ready", true /*will_reply*/);
diff --git a/chrome/browser/extensions/back_forward_cache_browsertest.cc b/chrome/browser/extensions/back_forward_cache_browsertest.cc
index cafd0851..a3980e2 100644
--- a/chrome/browser/extensions/back_forward_cache_browsertest.cc
+++ b/chrome/browser/extensions/back_forward_cache_browsertest.cc
@@ -474,6 +474,21 @@
   // 4) Expect that A is in the back forward cache.
   EXPECT_EQ(rfh_a->GetLifecycleState(),
             content::RenderFrameHost::LifecycleState::kInBackForwardCache);
+
+  // 5) Ensure that the runtime.onConnect listener in the restored page still
+  // works.
+  constexpr char kScript[] =
+      R"HTML(
+      var p;
+      chrome.tabs.query({}, (t) => {
+        p = chrome.tabs.connect(t[0].id);
+        p.onMessage.addListener(
+         (m) => {window.domAutomationController.send(m)}
+        );
+      });
+    )HTML";
+  EXPECT_EQ("connected",
+            ExecuteScriptInBackgroundPage(extension->id(), kScript));
 }
 
 // Test if the chrome.runtime.connect is called then disconnected, the page is
diff --git a/chrome/browser/extensions/content_script_tracker_browsertest.cc b/chrome/browser/extensions/content_script_tracker_browsertest.cc
index c3e80a9..680de88 100644
--- a/chrome/browser/extensions/content_script_tracker_browsertest.cc
+++ b/chrome/browser/extensions/content_script_tracker_browsertest.cc
@@ -305,20 +305,24 @@
     ui_test_utils::NavigateToURLWithDisposition(
         browser(), injected_url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
         ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
-    ASSERT_TRUE(listener.WaitUntilSatisfied());
-  }
-  content::WebContents* second_tab =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_NE(first_tab, second_tab);
+    content::WebContents* second_tab =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    EXPECT_NE(first_tab, second_tab);
 
-  // Verify that the new tab shows up as having been injected with content
-  // scripts.
-  EXPECT_EQ("content script has run",
-            content::EvalJs(second_tab, "document.body.innerText"));
+    // Verify that content script has been injected.
+    ASSERT_TRUE(listener.WaitUntilSatisfied());
+    EXPECT_EQ("content script has run",
+              content::EvalJs(second_tab, "document.body.innerText"));
+
+    // Verify that ContentScriptTracker detected the injection.
+    EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
+        *second_tab->GetMainFrame()->GetProcess(), extension->id()));
+  }
+
+  // Verify that the initial tab still is still correctly absent from
+  // ContentScriptTracker.
   EXPECT_EQ("This page has no title.",
             content::EvalJs(first_tab, "document.body.innerText"));
-  EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
-      *second_tab->GetMainFrame()->GetProcess(), extension->id()));
   EXPECT_FALSE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
       *first_tab->GetMainFrame()->GetProcess(), extension->id()));
 }
@@ -364,21 +368,23 @@
 
   // Navigate to a test page that *is* covered by `content_scripts.matches`
   // manifest entry above.
+  content::WebContents* first_tab = nullptr;
   {
     GURL injected_url =
         embedded_test_server()->GetURL("bar.com", "/title1.html");
     ExtensionTestMessageListener listener("Hello from content script!", false);
     ui_test_utils::NavigateToURL(browser(), injected_url);
-    ASSERT_TRUE(listener.WaitUntilSatisfied());
-  }
 
-  // Verify that ContentScriptTracker properly covered the initial frame.
-  content::WebContents* first_tab =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_EQ("content script has run",
-            content::EvalJs(first_tab, "document.body.innerText"));
-  EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
-      *first_tab->GetMainFrame()->GetProcess(), extension->id()));
+    // Verify that content script has been injected.
+    ASSERT_TRUE(listener.WaitUntilSatisfied());
+    first_tab = browser()->tab_strip_model()->GetActiveWebContents();
+    EXPECT_EQ("content script has run",
+              content::EvalJs(first_tab, "document.body.innerText"));
+
+    // Verify that ContentScriptTracker detected the injection.
+    EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
+        *first_tab->GetMainFrame()->GetProcess(), extension->id()));
+  }
 
   // Add a new subframe with a `data:...` URL.  This will verify that the
   // browser-side ContentScriptTracker correctly accounts for the renderer-side
@@ -391,28 +397,31 @@
         document.body.appendChild(iframe);
     )";
     ExecuteScriptAsync(first_tab, kScript);
-    ASSERT_TRUE(listener.WaitUntilSatisfied());
-  }
 
-  // Verify that ContentScriptTracker properly covered the new child frame (and
-  // continues to correctly cover the initial frame).
-  //
-  // The verification below is a bit redundant, because `main_frame` and
-  // `child_frame` are currently hosted in the same process, but this kind of
-  // verification is important if 1( we ever consider going back to per-frame
-  // tracking or 2) we start isolating opaque-origin/sandboxed frames into a
-  // separate process (tracked in https://crbug.com/510122).
-  content::RenderFrameHost* main_frame = first_tab->GetMainFrame();
-  content::RenderFrameHost* child_frame = content::ChildFrameAt(main_frame, 0);
-  ASSERT_TRUE(child_frame);
-  EXPECT_EQ("content script has run",
-            content::EvalJs(main_frame, "document.body.innerText"));
-  EXPECT_EQ("content script has run",
-            content::EvalJs(child_frame, "document.body.innerText"));
-  EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
-      *main_frame->GetProcess(), extension->id()));
-  EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
-      *child_frame->GetProcess(), extension->id()));
+    // Verify that content script has been injected.
+    ASSERT_TRUE(listener.WaitUntilSatisfied());
+    content::RenderFrameHost* main_frame = first_tab->GetMainFrame();
+    content::RenderFrameHost* child_frame =
+        content::ChildFrameAt(main_frame, 0);
+    ASSERT_TRUE(child_frame);
+    EXPECT_EQ("content script has run",
+              content::EvalJs(main_frame, "document.body.innerText"));
+    EXPECT_EQ("content script has run",
+              content::EvalJs(child_frame, "document.body.innerText"));
+
+    // Verify that ContentScriptTracker properly covered the new child frame
+    // (and continues to correctly cover the initial frame).
+    //
+    // The verification below is a bit redundant, because `main_frame` and
+    // `child_frame` are currently hosted in the same process, but this kind of
+    // verification is important if 1( we ever consider going back to per-frame
+    // tracking or 2) we start isolating opaque-origin/sandboxed frames into a
+    // separate process (tracked in https://crbug.com/510122).
+    EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
+        *main_frame->GetProcess(), extension->id()));
+    EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
+        *child_frame->GetProcess(), extension->id()));
+  }
 }
 
 // Covers detecting content script injection into 'about:blank'.
@@ -443,47 +452,50 @@
 
   // Navigate to a test page that *is* covered by `content_scripts.matches`
   // manifest entry above.
+  content::WebContents* first_tab = nullptr;
   {
     GURL injected_url =
         embedded_test_server()->GetURL("bar.com", "/title1.html");
     ExtensionTestMessageListener listener("Hello from content script!", false);
     ui_test_utils::NavigateToURL(browser(), injected_url);
-    ASSERT_TRUE(listener.WaitUntilSatisfied());
-  }
 
-  // Verify that ContentScriptTracker properly covered the initial frame.
-  content::WebContents* first_tab =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_EQ("content script has run",
-            content::EvalJs(first_tab, "document.body.innerText"));
-  EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
-      *first_tab->GetMainFrame()->GetProcess(), extension->id()));
+    // Verify that content script has been injected.
+    ASSERT_TRUE(listener.WaitUntilSatisfied());
+    first_tab = browser()->tab_strip_model()->GetActiveWebContents();
+    EXPECT_EQ("content script has run",
+              content::EvalJs(first_tab, "document.body.innerText"));
+
+    // Verify that ContentScriptTracker properly covered the initial frame.
+    EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
+        *first_tab->GetMainFrame()->GetProcess(), extension->id()));
+  }
 
   // Open a new tab with 'about:blank'.  This may be tricky, because the initial
   // 'about:blank' navigation will not go through ReadyToCommit state.
-  content::WebContents* popup = nullptr;
   {
     ExtensionTestMessageListener listener("Hello from content script!", false);
     content::WebContentsAddedObserver popup_observer;
     ASSERT_TRUE(ExecJs(first_tab, "window.open('about:blank', '_blank')"));
-    popup = popup_observer.GetWebContents();
+    content::WebContents* popup = popup_observer.GetWebContents();
     WaitForLoadStop(popup);
-    ASSERT_TRUE(listener.WaitUntilSatisfied());
-  }
 
-  // Verify that ContentScriptTracker properly covered the popup (and continues
-  // to correctly cover the initial frame).  The verification below is a bit
-  // redundant, because `first_tab` and `popup` are hosted in the same process,
-  // but this kind of verification is important if we ever consider going back
-  // to per-frame tracking.
-  EXPECT_EQ("content script has run",
-            content::EvalJs(first_tab, "document.body.innerText"));
-  EXPECT_EQ("content script has run",
-            content::EvalJs(popup, "document.body.innerText"));
-  EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
-      *first_tab->GetMainFrame()->GetProcess(), extension->id()));
-  EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
-      *popup->GetMainFrame()->GetProcess(), extension->id()));
+    // Verify that content script has been injected.
+    ASSERT_TRUE(listener.WaitUntilSatisfied());
+    EXPECT_EQ("content script has run",
+              content::EvalJs(first_tab, "document.body.innerText"));
+    EXPECT_EQ("content script has run",
+              content::EvalJs(popup, "document.body.innerText"));
+
+    // Verify that ContentScriptTracker properly covered the popup (and
+    // continues to correctly cover the initial frame).  The verification below
+    // is a bit redundant, because `first_tab` and `popup` are hosted in the
+    // same process, but this kind of verification is important if we ever
+    // consider going back to per-frame tracking.
+    EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
+        *first_tab->GetMainFrame()->GetProcess(), extension->id()));
+    EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
+        *popup->GetMainFrame()->GetProcess(), extension->id()));
+  }
 }
 
 // Covers detecting content script injection into an initial empty document.
@@ -535,21 +547,23 @@
 
   // Navigate to a test page that *is* covered by `content_scripts.matches`
   // manifest entry above.
+  content::WebContents* first_tab = nullptr;
   {
     GURL injected_url =
         embedded_test_server()->GetURL("bar.com", "/title1.html");
     ExtensionTestMessageListener listener("Hello from content script!", false);
     ui_test_utils::NavigateToURL(browser(), injected_url);
-    ASSERT_TRUE(listener.WaitUntilSatisfied());
-  }
 
-  // Verify that ContentScriptTracker properly covered the initial frame.
-  content::WebContents* first_tab =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_EQ("content script has run: 1",
-            content::EvalJs(first_tab, "document.body.innerText"));
-  EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
-      *first_tab->GetMainFrame()->GetProcess(), extension->id()));
+    // Verify that content script has been injected.
+    ASSERT_TRUE(listener.WaitUntilSatisfied());
+    first_tab = browser()->tab_strip_model()->GetActiveWebContents();
+    EXPECT_EQ("content script has run: 1",
+              content::EvalJs(first_tab, "document.body.innerText"));
+
+    // Verify that ContentScriptTracker properly covered the initial frame.
+    EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
+        *first_tab->GetMainFrame()->GetProcess(), extension->id()));
+  }
 
   // Add a new subframe with `src=javascript:...` attribute.  This will leave
   // the subframe at the initial empty document (no navigation / no
@@ -726,24 +740,70 @@
     ui_test_utils::NavigateToURLWithDisposition(
         browser(), injected_url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
         ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
-    ASSERT_TRUE(listener.WaitUntilSatisfied());
-  }
-  content::WebContents* second_tab =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_NE(first_tab, second_tab);
 
-  // Verify that the new tab shows up as having been injected with content
-  // scripts.
-  EXPECT_EQ("content script has run",
-            content::EvalJs(second_tab, "document.body.innerText"));
+    // Verify that content script has been injected.
+    ASSERT_TRUE(listener.WaitUntilSatisfied());
+    content::WebContents* second_tab =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    EXPECT_NE(first_tab, second_tab);
+    EXPECT_EQ("content script has run",
+              content::EvalJs(second_tab, "document.body.innerText"));
+
+    // Verify that ContentScriptTracker detected the injection.
+    EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
+        *second_tab->GetMainFrame()->GetProcess(), extension->id()));
+  }
+
+  // Verify that still no content script has been run in the `first_tab`.
   EXPECT_EQ("This page has no title.",
             content::EvalJs(first_tab, "document.body.innerText"));
-  EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
-      *second_tab->GetMainFrame()->GetProcess(), extension->id()));
   EXPECT_FALSE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
       *first_tab->GetMainFrame()->GetProcess(), extension->id()));
 }
 
+IN_PROC_BROWSER_TEST_F(ContentScriptTrackerBrowserTest, HistoryPushState) {
+  // Install a test extension.
+  TestExtensionDir dir;
+  const char kManifestTemplate[] = R"(
+      {
+        "name": "ContentScriptTrackerBrowserTest - Declarative",
+        "version": "1.0",
+        "manifest_version": 2,
+        "permissions": [ "tabs", "<all_urls>" ],
+        "content_scripts": [{
+          "all_frames": true,
+          "matches": ["*://bar.com/pushed_url.html"],
+          "js": ["content_script.js"],
+          "run_at": "document_end"
+        }]
+      } )";
+  dir.WriteManifest(kManifestTemplate);
+  dir.WriteFile(FILE_PATH_LITERAL("content_script.js"), R"(
+                document.body.innerText = 'content script has run';
+                chrome.test.sendMessage('Hello from content script!'); )");
+  const Extension* extension = LoadExtension(dir.UnpackedPath());
+  ASSERT_TRUE(extension);
+
+  // Navigate to a test page that is *not* covered by the URL patterns above,
+  // but that immediately executes `history.pushState` that changes the URL
+  // to one that *is* covered by the URL patterns above.
+  GURL url =
+      embedded_test_server()->GetURL("bar.com", "/History/push_state.html");
+  ExtensionTestMessageListener listener("Hello from content script!", false);
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  // Verify that content script has been injected.
+  ASSERT_TRUE(listener.WaitUntilSatisfied());
+  content::RenderFrameHost* main_frame =
+      browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame();
+  EXPECT_EQ("content script has run",
+            content::EvalJs(main_frame, "document.body.innerText"));
+
+  // Verify that ContentScriptTracker detected the injection.
+  EXPECT_TRUE(ContentScriptTracker::DidProcessRunContentScriptFromExtension(
+      *main_frame->GetProcess(), extension->id()));
+}
+
 class ContentScriptTrackerAppBrowserTest : public PlatformAppBrowserTest {
  public:
   ContentScriptTrackerAppBrowserTest() = default;
diff --git a/chrome/browser/extensions/cross_origin_isolation_browsertest.cc b/chrome/browser/extensions/cross_origin_isolation_browsertest.cc
index 97cb330..0014f407 100644
--- a/chrome/browser/extensions/cross_origin_isolation_browsertest.cc
+++ b/chrome/browser/extensions/cross_origin_isolation_browsertest.cc
@@ -21,6 +21,7 @@
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
 #include "extensions/test/test_extension_dir.h"
+#include "net/dns/mock_host_resolver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace extensions {
@@ -41,6 +42,12 @@
   CrossOriginIsolationTest(const CrossOriginIsolationTest&) = delete;
   CrossOriginIsolationTest& operator=(const CrossOriginIsolationTest&) = delete;
 
+  void SetUpOnMainThread() override {
+    ExtensionBrowserTest::SetUpOnMainThread();
+    host_resolver()->AddRule("*", "127.0.0.1");
+    ASSERT_TRUE(embedded_test_server()->Start());
+  }
+
   const Extension* LoadExtension(TestExtensionDir& dir,
                                  const char* coep_value,
                                  const char* coop_value,
@@ -62,7 +69,8 @@
         "web_accessible_resources": ["test.html"],
         "browser_action": {
           "default_title": "foo"
-        }
+        },
+        "permissions": ["http://foo.test:*/*"]
       }
     )";
 
@@ -141,8 +149,6 @@
 // Tests that a web accessible frame from a cross origin isolated extension is
 // not cross origin isolated.
 IN_PROC_BROWSER_TEST_F(CrossOriginIsolationTest, WebAccessibleFrame) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
   RestrictProcessCount();
 
   TestExtensionDir coi_test_dir;
@@ -217,6 +223,35 @@
                   coi_extension, extension_iframe->GetProcess()->GetID(), url));
   }
 
+  // Ensure both cross-origin-isolated and non-cross-origin-isolated extension
+  // contexts inherit extension's cross-origin privileges.
+  {
+    auto execute_fetch = [](content::RenderFrameHost* host, const GURL& url) {
+      const char* kScript = R"(
+        fetch('%s')
+          .then(response => response.text())
+          .then(text => window.domAutomationController.send(text))
+          .catch(err => window.domAutomationController.send(
+            "Fetch error: " + err));
+      )";
+      std::string script = base::StringPrintf(kScript, url.spec().c_str());
+      std::string result;
+      if (!content::ExecuteScriptAndExtractString(host, script, &result))
+        return std::string("Error executing script");
+      return result;
+    };
+    // Sanity check that fetching a url the extension doesn't have access to,
+    // leads to a fetch error.
+    const char* kPath = "/extensions/test_file.txt";
+    GURL disallowed_url = embedded_test_server()->GetURL("bar.test", kPath);
+    EXPECT_THAT(execute_fetch(coi_background_rfh, disallowed_url),
+                ::testing::HasSubstr("Fetch error:"));
+
+    GURL allowed_url = embedded_test_server()->GetURL("foo.test", kPath);
+    EXPECT_EQ("Hello!", execute_fetch(coi_background_rfh, allowed_url));
+    EXPECT_EQ("Hello!", execute_fetch(extension_iframe, allowed_url));
+  }
+
   // Finally make some extension API calls to ensure both cross-origin-isolated
   // and non-cross-origin-isolated extension contexts are considered "blessed".
   {
@@ -246,8 +281,6 @@
 // Test that an extension service worker for a cross origin isolated extension
 // is not cross origin isolated. See crbug.com/1131404.
 IN_PROC_BROWSER_TEST_F(CrossOriginIsolationTest, ServiceWorker) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
   RestrictProcessCount();
 
   constexpr char kServiceWorkerScript[] = R"(
@@ -311,8 +344,6 @@
 // isolated contexts.
 IN_PROC_BROWSER_TEST_F(CrossOriginIsolationTest,
                        WebAccessibleFrame_WindowApis) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
   TestExtensionDir coi_test_dir;
   const Extension* coi_extension =
       LoadExtension(coi_test_dir, "require-corp", "same-origin");
@@ -396,8 +427,6 @@
 // Tests extension messaging between cross origin isolated and
 // non-cross-origin-isolated frames of an extension.
 IN_PROC_BROWSER_TEST_F(CrossOriginIsolationTest, ExtensionMessaging_Frames) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
   RestrictProcessCount();
 
   constexpr char kTestJs[] = R"(
@@ -487,8 +516,6 @@
 // a different process).
 IN_PROC_BROWSER_TEST_F(CrossOriginIsolationTest,
                        ExtensionMessaging_ServiceWorker) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
   RestrictProcessCount();
 
   constexpr char kTestJs[] = R"(
diff --git a/chrome/browser/extensions/extension_web_ui_unittest.cc b/chrome/browser/extensions/extension_web_ui_unittest.cc
index 121d3d9..404a48c 100644
--- a/chrome/browser/extensions/extension_web_ui_unittest.cc
+++ b/chrome/browser/extensions/extension_web_ui_unittest.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_web_ui_override_registrar.h"
 #include "chrome/browser/extensions/test_extension_system.h"
+#include "chrome/common/extensions/api/chrome_url_overrides.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/favicon_base/favicon_callback.h"
@@ -86,7 +87,7 @@
   manifest.Set(manifest_keys::kName, "ext1")
       .Set(manifest_keys::kVersion, "0.1")
       .Set(manifest_keys::kManifestVersion, 2)
-      .Set(std::string(manifest_keys::kChromeURLOverrides),
+      .Set(api::chrome_url_overrides::ManifestKeys::kChromeUrlOverrides,
            DictionaryBuilder().Set("bookmarks", kOverrideResource).Build());
   scoped_refptr<const Extension> ext_unpacked(
       ExtensionBuilder()
@@ -122,7 +123,7 @@
   manifest2.Set(manifest_keys::kName, "ext2")
       .Set(manifest_keys::kVersion, "0.1")
       .Set(manifest_keys::kManifestVersion, 2)
-      .Set(std::string(manifest_keys::kChromeURLOverrides),
+      .Set(api::chrome_url_overrides::ManifestKeys::kChromeUrlOverrides,
            DictionaryBuilder().Set("bookmarks", kOverrideResource2).Build());
   scoped_refptr<const Extension> ext_component(
       ExtensionBuilder()
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 9761086..9096d98 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -4360,6 +4360,11 @@
     "expiry_milestone": 95
   },
   {
+    "name": "pdf-unseasoned",
+    "owners": [ "dhoss", "kmoon", "nigi", "thestig"],
+    "expiry_milestone": 97
+  },
+  {
     "name": "pdf-xfa-forms",
     "owners": [ "carlosil", "tsepez" ],
     "expiry_milestone": 94
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 7bbe5762..a1cf822 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2041,6 +2041,9 @@
 const char kPasswordScriptsFetchingDescription[] =
     "Fetches scripts for password change flows.";
 
+const char kPdfUnseasonedName[] = "Pepper-free PDF viewer";
+const char kPdfUnseasonedDescription[] = "Enables the Pepper-free PDF viewer.";
+
 const char kPdfXfaFormsName[] = "PDF XFA support";
 const char kPdfXfaFormsDescription[] =
     "Enables support for XFA forms in PDFs. "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 2b0eb40f7..5beb6b8 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1166,6 +1166,9 @@
 extern const char kPasswordScriptsFetchingName[];
 extern const char kPasswordScriptsFetchingDescription[];
 
+extern const char kPdfUnseasonedName[];
+extern const char kPdfUnseasonedDescription[];
+
 extern const char kPdfXfaFormsName[];
 extern const char kPdfXfaFormsDescription[];
 
diff --git a/chrome/browser/flags/BUILD.gn b/chrome/browser/flags/BUILD.gn
index b0bea8d..45ceca1 100644
--- a/chrome/browser/flags/BUILD.gn
+++ b/chrome/browser/flags/BUILD.gn
@@ -10,6 +10,7 @@
     "android/java/src/org/chromium/chrome/browser/flags/BooleanCachedFieldTrialParameter.java",
     "android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java",
     "android/java/src/org/chromium/chrome/browser/flags/CachedFieldTrialParameter.java",
+    "android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java",
     "android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java",
     "android/java/src/org/chromium/chrome/browser/flags/ChromeSessionState.java",
     "android/java/src/org/chromium/chrome/browser/flags/DoubleCachedFieldTrialParameter.java",
@@ -23,6 +24,7 @@
     "//base:base_java",
     "//build:chromeos_buildflags",
     "//chrome/browser/preferences:java",
+    "//chrome/browser/version:java",
     "//third_party/androidx:androidx_annotation_annotation_java",
   ]
   srcjar_deps = [
@@ -69,6 +71,7 @@
   testonly = true
   sources = [
     "android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsAnnotationUnitTest.java",
+    "android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java",
     "android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsUnitTest.java",
     "android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureListWithProcessorUnitTest.java",
     "android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureListWithoutProcessorUnitTest.java",
@@ -80,6 +83,7 @@
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
     "//base/test:test_support_java",
+    "//chrome/browser/preferences:java",
     "//chrome/test/android:chrome_java_test_support",
     "//third_party/android_deps:robolectric_all_java",
     "//third_party/junit",
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index ea850bd..68f1ef1b 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -12,6 +12,7 @@
 #include "base/android/jni_string.h"
 #include "base/feature_list.h"
 #include "base/metrics/field_trial_params.h"
+#include "chrome/browser/browser_features.h"
 #include "chrome/browser/commerce/commerce_feature_list.h"
 #include "chrome/browser/flags/jni_headers/ChromeFeatureList_jni.h"
 #include "chrome/browser/notifications/chime/android/features.h"
@@ -115,6 +116,7 @@
     &features::kPrivacySandboxSettings,
     &features::kPrivacySandboxSettings2,
     &features::kPrioritizeBootstrapTasks,
+    &features::kPwaUpdateDialogForNameAndIcon,
     &features::kQuietNotificationPrompts,
     &features::kRequestDesktopSiteForTablets,
     &features::kSearchHistoryLink,
@@ -219,7 +221,6 @@
     &kOfflineMeasurementsBackgroundTask,
     &kPageAnnotationsService,
     &kProbabilisticCryptidRenderer,
-    &kPwaUpdateDialogForNameAndIcon,
     &kQuickActionSearchWidgetAndroid,
     &kReachedCodeProfiler,
     &kReaderModeInCCT,
@@ -611,9 +612,6 @@
 const base::Feature kProbabilisticCryptidRenderer{
     "ProbabilisticCryptidRenderer", base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kPwaUpdateDialogForNameAndIcon{
-    "PwaUpdateDialogForNameAndIcon", base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kQuickActionSearchWidgetAndroid{
     "QuickActionSearchWidgetAndroid", base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index b6b15089..186bc00 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -101,7 +101,6 @@
 extern const base::Feature kOfflineIndicatorV2;
 extern const base::Feature kOfflineMeasurementsBackgroundTask;
 extern const base::Feature kPageAnnotationsService;
-extern const base::Feature kPwaUpdateDialogForNameAndIcon;
 extern const base::Feature kProbabilisticCryptidRenderer;
 extern const base::Feature kQuickActionSearchWidgetAndroid;
 extern const base::Feature kReachedCodeProfiler;
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
index eaa9d9a4..6d65011 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
@@ -127,6 +127,7 @@
 
     private static ValuesReturned sValuesReturned = new ValuesReturned();
     private static ValuesOverridden sValuesOverridden = new ValuesOverridden();
+    private static CachedFlagsSafeMode sSafeMode = new CachedFlagsSafeMode();
 
     private static String sReachedCodeProfilerTrialGroup;
 
@@ -155,6 +156,8 @@
                     "Feature " + featureName + " has no default in CachedFeatureFlags.");
         }
 
+        sSafeMode.onFlagChecked();
+
         String preferenceName = getPrefForFeatureFlag(featureName);
 
         Boolean flag = sValuesReturned.boolMap().get(preferenceName);
@@ -318,7 +321,34 @@
         return sReachedCodeProfilerTrialGroup;
     }
 
+    /**
+     * Call when entering an initialization flow that should result in caching flags.
+     */
+    public static void onStartOrResumeCheckpoint() {
+        sSafeMode.onStartOrResumeCheckpoint();
+    }
+
+    /**
+     * Call when aborting an initialization flow that would have resulted in caching flags.
+     */
+    public static void onPauseCheckpoint() {
+        sSafeMode.onPauseCheckpoint();
+    }
+
+    /**
+     * Call when finishing an initialization flow with flags having been cached successfully.
+     */
+    public static void onEndCheckpoint() {
+        sSafeMode.onEndCheckpoint(sValuesReturned);
+    }
+
+    public static @CachedFlagsSafeMode.Behavior int getSafeModeBehaviorForTesting() {
+        return sSafeMode.getBehaviorForTesting();
+    }
+
     static boolean getConsistentBooleanValue(String preferenceName, boolean defaultValue) {
+        sSafeMode.onFlagChecked();
+
         if (sValuesOverridden.isEnabled()) {
             return sValuesOverridden.getBool(preferenceName, defaultValue);
         }
@@ -332,6 +362,8 @@
     }
 
     static String getConsistentStringValue(String preferenceName, String defaultValue) {
+        sSafeMode.onFlagChecked();
+
         if (sValuesOverridden.isEnabled()) {
             return sValuesOverridden.getString(preferenceName, defaultValue);
         }
@@ -345,6 +377,8 @@
     }
 
     static int getConsistentIntValue(String preferenceName, int defaultValue) {
+        sSafeMode.onFlagChecked();
+
         if (sValuesOverridden.isEnabled()) {
             return sValuesOverridden.getInt(preferenceName, defaultValue);
         }
@@ -358,6 +392,8 @@
     }
 
     static double getConsistentDoubleValue(String preferenceName, double defaultValue) {
+        sSafeMode.onFlagChecked();
+
         if (sValuesOverridden.isEnabled()) {
             return sValuesOverridden.getDouble(preferenceName, defaultValue);
         }
@@ -383,6 +419,7 @@
     public static void resetFlagsForTesting() {
         sValuesReturned.clear();
         sValuesOverridden.clear();
+        sSafeMode.clearForTesting();
     }
 
     @VisibleForTesting
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java
new file mode 100644
index 0000000..c45a6d8
--- /dev/null
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java
@@ -0,0 +1,339 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.flags;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.FeatureList;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.flags.CachedFlagsSafeMode.Behavior;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Unit Tests for the Safe Mode mechanism for {@link CachedFeatureFlags}.
+ *
+ * Tests the public API {@link CachedFeatureFlags} rather than the implementation
+ * {@link CachedFlagsSafeMode}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+public class CachedFeatureFlagsSafeModeUnitTest {
+    private static final String CRASHY_FEATURE = "FeatureA";
+    private static final String OK_FEATURE = "FeatureB";
+
+    Map<String, Boolean> mDefaultsSwapped;
+
+    @Before
+    public void setUp() {
+        CachedFeatureFlags.resetFlagsForTesting();
+        Map<String, Boolean> defaults = makeFeatureMap(false, false);
+        mDefaultsSwapped = CachedFeatureFlags.swapDefaultsForTesting(defaults);
+    }
+
+    @After
+    public void tearDown() {
+        CachedFeatureFlags.resetFlagsForTesting();
+        CachedFeatureFlags.swapDefaultsForTesting(mDefaultsSwapped);
+
+        FeatureList.setTestFeatures(null);
+    }
+
+    @Test
+    public void testTwoCrashesInARow_engageSafeMode() {
+        startRun();
+        // Crash streak is 0. Do not engage Safe Mode.
+        // There are no safe values.
+        // There are no cached flag values, so the defaults false/false are used.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertFalse(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertFalse(CachedFeatureFlags.isEnabled(OK_FEATURE));
+        endCleanRun(false, true);
+        // Safe values became false/false.
+        // Cached values became false/true.
+
+        startRun();
+        // Crash streak is 0. Do not engage Safe Mode.
+        // Safe values are false/false.
+        // Cached flag values are false/true, from previous run.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertFalse(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertTrue(CachedFeatureFlags.isEnabled(OK_FEATURE));
+        endCleanRun(true, true);
+        // Safe values became false/true.
+        // Cached values became true(crashy)/true.
+
+        startRun();
+        // Crash streak is 0. Do not engage Safe Mode.
+        // Safe values are false/true.
+        // Cached values remain true(crashy)/true and are used.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertTrue(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertTrue(CachedFeatureFlags.isEnabled(OK_FEATURE));
+        endCrashyRun();
+        // Cached values remain true(crashy)/true.
+
+        startRun();
+        // Crash streak is 1. Do not engage Safe Mode.
+        // Safe values are false/true.
+        // Cached values remain true(crashy)/true and are used.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertTrue(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertTrue(CachedFeatureFlags.isEnabled(OK_FEATURE));
+        endCrashyRun();
+        // Cached values remain true(crashy)/true.
+
+        startRun();
+        // Crash streak is 2. Engage Safe Mode.
+        // Safe values are false/true, and are used during this run.
+        // Cached values remain true(crashy)/true, but are not used because Safe Mode is engaged.
+        assertEquals(Behavior.ENGAGED_WITH_SAFE_VALUES,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        // TODO(crbug.com/1217708): Assert cached flags values are false/true.
+        endCleanRun(false, false);
+        // Cached values became false/false, cached from native.
+
+        startRun();
+        // Crash streak is 0. Do not engage Safe Mode.
+        // Safe values are false/true still.
+        // Cached values false/false are used, cached from native last run.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        // TODO(crbug.com/1217708): Assert cached flags values are false/false.
+    }
+
+    @Test
+    public void testTwoCrashesInterrupted_normalMode() {
+        startRun();
+        // Crash streak is 0. Do not engage Safe Mode.
+        // There are no safe values.
+        // There are no cached flag values, so the defaults false/false are used.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertFalse(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertFalse(CachedFeatureFlags.isEnabled(OK_FEATURE));
+        endCleanRun(true, true);
+        // Safe values became false/false.
+        // Cached values became true(flaky)/true.
+
+        startRun();
+        // Crash streak is 0. Do not engage Safe Mode.
+        // Safe values are false/false.
+        // Cached flag values are true(flaky)/true.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertTrue(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertTrue(CachedFeatureFlags.isEnabled(OK_FEATURE));
+        endCrashyRun();
+        // Cached values remain true(crashy)/true.
+
+        startRun();
+        // Crash streak is 1. Do not engage Safe Mode.
+        // Safe values are false/false.
+        // Cached flag values are true(flaky)/true.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        // Cached flag values are the flaky ones cached from native.
+        assertTrue(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertTrue(CachedFeatureFlags.isEnabled(OK_FEATURE));
+        endCleanRun(true, true);
+        // Safe values became true(flaky)/true.
+        // Cached values remain true(flaky)/true.
+
+        startRun();
+        // Crash streak is 0, do not engage, use flaky values.
+        // Safe values are true(flaky)/true.
+        // Cached flag values are true(flaky)/true.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertTrue(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertTrue(CachedFeatureFlags.isEnabled(OK_FEATURE));
+    }
+
+    /**
+     * Tests that decrementing the crash streak to account for an aborted run prevents Safe Mode
+     * from engaging.
+     */
+    @Test
+    public void testTwoFREs_normalMode() {
+        startRun();
+        // Crash streak is 0. Do not engage Safe Mode.
+        // There are no safe values.
+        // There are no cached flag values, so the defaults false/false are used.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertFalse(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertFalse(CachedFeatureFlags.isEnabled(OK_FEATURE));
+        endFirstRunWithKill();
+
+        startRun();
+        // Crash streak is 0. Do not engage Safe Mode.
+        // There are no safe values.
+        // There are no cached flag values, so the defaults false/false are used.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertFalse(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertFalse(CachedFeatureFlags.isEnabled(OK_FEATURE));
+        endFirstRunWithKill();
+
+        startRun();
+        // Crash streak is 0, do not engage, use flaky values.
+        // There are no safe values.
+        // There are no cached flag values, so the defaults false/false are used.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertFalse(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertFalse(CachedFeatureFlags.isEnabled(OK_FEATURE));
+    }
+
+    @Test
+    public void testTwoCrashesInARow_engageSafeModeWithoutSafeValues() {
+        // Simulate a cache without writing safe values. This happens before Safe Mode was
+        // implemented and will become rare as clients start writing safe values.
+        // Cache a crashy value.
+        FeatureList.setTestFeatures(makeFeatureMap(true, true));
+        CachedFeatureFlags.cacheNativeFlags(Arrays.asList(CRASHY_FEATURE, OK_FEATURE));
+        CachedFeatureFlags.resetFlagsForTesting();
+        // Cached values became true(crashy)/true.
+
+        startRun();
+        // Crash streak is 0. Do not engage Safe Mode.
+        // There are no safe values.
+        // Cached values are true(crashy)/true.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertTrue(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertTrue(CachedFeatureFlags.isEnabled(OK_FEATURE));
+        endCrashyRun();
+        // Cached values remain true(crashy)/true.
+
+        startRun();
+        // Crash streak is 1. Do not engage Safe Mode.
+        // There are no safe values.
+        // Cached values are true(crashy)/true.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertTrue(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertTrue(CachedFeatureFlags.isEnabled(OK_FEATURE));
+        endCrashyRun();
+        // Cached values remain true(crashy)/true.
+
+        startRun();
+        // Crash streak is 2. Engage Safe Mode without safe values.
+        // There are no safe values.
+        // Cached values are true(crashy)/true, but the default values false/false are returned
+        // since Safe Mode is falling back to default.
+        assertEquals(Behavior.ENGAGED_WITHOUT_SAFE_VALUES,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        // TODO(crbug.com/1217708): Assert cached flags values are false/false.
+    }
+
+    @Test
+    public void testTwoCrashesInARow_engageSafeModeIgnoringOutdated() {
+        startRun();
+        // Crash streak is 0. Do not engage Safe Mode.
+        // There are no safe values.
+        // There are no cached flag values, so the defaults false/false are used.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertFalse(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertFalse(CachedFeatureFlags.isEnabled(OK_FEATURE));
+        endCleanRun(false, true);
+        // Safe values became false/false.
+        // Cached values became false/true.
+
+        startRun();
+        // Crash streak is 0. Do not engage Safe Mode.
+        // Safe values are false/false.
+        // Cached flag values are false/true, from previous run.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertFalse(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertTrue(CachedFeatureFlags.isEnabled(OK_FEATURE));
+        endCleanRun(true, true);
+        // Safe values became false/true.
+        // Cached values became true(crashy)/true.
+
+        // Pretend safe values are from an older version
+        SharedPreferencesManager.getInstance().writeString(
+                ChromePreferenceKeys.FLAGS_CACHED_SAFE_VALUES_VERSION, "1.0.0.0");
+
+        startRun();
+        // Crash streak is 0. Do not engage Safe Mode.
+        // Safe values are false/true, but from another version.
+        // Cached values are true(crashy)/true.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertTrue(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertTrue(CachedFeatureFlags.isEnabled(OK_FEATURE));
+        endCrashyRun();
+        // Cached values remain true(crashy)/true.
+
+        startRun();
+        // Crash streak is 1. Do not engage Safe Mode.
+        // Safe values are false/true, but from another version.
+        // Cached values are true(crashy)/true.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertTrue(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertTrue(CachedFeatureFlags.isEnabled(OK_FEATURE));
+        endCrashyRun();
+        // Cached values remain true(crashy)/true.
+
+        startRun();
+        assertEquals(Behavior.ENGAGED_IGNORING_OUTDATED_SAFE_VALUES,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        // Crash streak is 2. Engage Safe Mode with obsolete safe values.
+        // Safe values are false/true, but from another version.
+        // Cached values are true(crashy)/true, but the default values false/false are returned
+        // since Safe Mode is falling back to default.
+        // TODO(crbug.com/1217708): Assert cached flags values are false/false.
+    }
+
+    private void startRun() {
+        CachedFeatureFlags.isEnabled(CRASHY_FEATURE);
+        CachedFeatureFlags.onStartOrResumeCheckpoint();
+    }
+
+    private void endFirstRunWithKill() {
+        CachedFeatureFlags.onPauseCheckpoint();
+        CachedFeatureFlags.resetFlagsForTesting();
+    }
+
+    private void endCrashyRun() {
+        CachedFeatureFlags.resetFlagsForTesting();
+    }
+
+    private void endCleanRun(boolean crashyFeatureValue, boolean okFeatureValue) {
+        FeatureList.setTestFeatures(makeFeatureMap(crashyFeatureValue, okFeatureValue));
+        CachedFeatureFlags.cacheNativeFlags(Arrays.asList(CRASHY_FEATURE, OK_FEATURE));
+        CachedFeatureFlags.onEndCheckpoint();
+        CachedFeatureFlags.resetFlagsForTesting();
+    }
+
+    private HashMap<String, Boolean> makeFeatureMap(
+            boolean crashyFeatureValue, boolean okFeatureValue) {
+        return new HashMap<String, Boolean>() {
+            {
+                put(CRASHY_FEATURE, crashyFeatureValue);
+                put(OK_FEATURE, okFeatureValue);
+            }
+        };
+    }
+}
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java
new file mode 100644
index 0000000..eab5972
--- /dev/null
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java
@@ -0,0 +1,187 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.flags;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.base.Log;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.chrome.browser.preferences.KeyPrefix;
+import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
+import org.chromium.chrome.browser.version.ChromeVersionInfo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Controls Safe Mode for {@link CachedFeatureFlags}.
+ *
+ * Safe Mode is a mechanism that allows Chrome to prevent crashes gated behind flags used before
+ * native from becoming a crash loop that cannot be recovered from by disabling the experiment.
+ *
+ * TODO(crbug.com/1217708): Safe mode at the moment does not engage. Validate the crash streak logic
+ * in Canary before turning it on.
+ */
+class CachedFlagsSafeMode {
+    private static final String TAG = "Flags";
+    private static final int CRASH_STREAK_TO_ENTER_SAFE_MODE = 2;
+
+    // These values are persisted to logs. Entries should not be renumbered and numeric values
+    // should never be reused.
+    @VisibleForTesting
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({Behavior.UNKNOWN, Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+            Behavior.ENGAGED_WITH_SAFE_VALUES, Behavior.ENGAGED_IGNORING_OUTDATED_SAFE_VALUES,
+            Behavior.ENGAGED_WITHOUT_SAFE_VALUES})
+    @interface Behavior {
+        int UNKNOWN = 0;
+        int NOT_ENGAGED_BELOW_THRESHOLD = 1;
+        int ENGAGED_WITH_SAFE_VALUES = 2;
+        int ENGAGED_IGNORING_OUTDATED_SAFE_VALUES = 3;
+        int ENGAGED_WITHOUT_SAFE_VALUES = 4;
+
+        int NUM_ENTRIES = 5;
+    }
+
+    private @Behavior int mBehavior = Behavior.UNKNOWN;
+
+    CachedFlagsSafeMode() {}
+
+    /**
+     * Call right before any flag is checked. The first time this is called, check if safe mode
+     * should be engaged, and engages it if necessary.
+     */
+    void onFlagChecked() {
+        if (mBehavior == Behavior.UNKNOWN) {
+            if (shouldEnterSafeMode()) {
+                String cachedVersion = SharedPreferencesManager.getInstance().readString(
+                        ChromePreferenceKeys.FLAGS_CACHED_SAFE_VALUES_VERSION, "");
+                if (cachedVersion.isEmpty()) {
+                    mBehavior = Behavior.ENGAGED_WITHOUT_SAFE_VALUES;
+                } else if (!cachedVersion.equals(ChromeVersionInfo.getProductVersion())) {
+                    mBehavior = Behavior.ENGAGED_IGNORING_OUTDATED_SAFE_VALUES;
+                } else {
+                    mBehavior = Behavior.ENGAGED_WITH_SAFE_VALUES;
+                }
+                RecordHistogram.recordEnumeratedHistogram(
+                        "Variations.SafeModeCachedFlags.Engaged", mBehavior, Behavior.NUM_ENTRIES);
+                engageSafeModeInNative();
+                restoreSafeValues();
+            } else {
+                mBehavior = Behavior.NOT_ENGAGED_BELOW_THRESHOLD;
+                RecordHistogram.recordEnumeratedHistogram(
+                        "Variations.SafeModeCachedFlags.Engaged", mBehavior, Behavior.NUM_ENTRIES);
+            }
+        }
+    }
+
+    /**
+     * Call at an early point in the path that leads to caching flags. If onFinishedCachingFlags()
+     * does not get called before the next run, this run will be considered a crash for purposes of
+     * counting the crash streak and entering Safe Mode.
+     */
+    public void onStartOrResumeCheckpoint() {
+        SharedPreferencesManager.getInstance().incrementInt(
+                ChromePreferenceKeys.FLAGS_CRASH_STREAK_BEFORE_CACHE);
+        RecordHistogram.recordEnumeratedHistogram(
+                "Variations.SafeModeCachedFlags.WillCache", mBehavior, Behavior.NUM_ENTRIES);
+    }
+
+    /**
+     * Call when aborting a path that leads to caching flags. Rolls back the crash streak
+     * incremented in {@link #onStartOrResumeCheckpoint} but does not reset it.
+     */
+    public void onPauseCheckpoint() {
+        int currentStreak = SharedPreferencesManager.getInstance().readInt(
+                ChromePreferenceKeys.FLAGS_CRASH_STREAK_BEFORE_CACHE);
+        assert currentStreak >= 0;
+        SharedPreferencesManager.getInstance().writeInt(
+                ChromePreferenceKeys.FLAGS_CRASH_STREAK_BEFORE_CACHE, currentStreak - 1);
+        RecordHistogram.recordEnumeratedHistogram(
+                "Variations.SafeModeCachedFlags.Pause", mBehavior, Behavior.NUM_ENTRIES);
+    }
+
+    /**
+     * Call when all flags have been cached. Signals that the current configuration is safe. It will
+     * be saved to be used in Safe Mode.
+     */
+    void onEndCheckpoint(ValuesReturned safeValuesReturned) {
+        SharedPreferencesManager.getInstance().writeInt(
+                ChromePreferenceKeys.FLAGS_CRASH_STREAK_BEFORE_CACHE, 0);
+        writeSafeValues(safeValuesReturned);
+        RecordHistogram.recordEnumeratedHistogram(
+                "Variations.SafeModeCachedFlags.Cached", mBehavior, Behavior.NUM_ENTRIES);
+    }
+
+    private void engageSafeModeInNative() {
+        // TODO(crbug.com/1217708): Notify native that a safe seed should be used.
+    }
+
+    private void restoreSafeValues() {
+        // TODO(crbug.com/1217708): Overwrite cached values with safe values.
+        // TODO(crbug.com/1217708): Ignore safe values from previous versions.
+        // TODO(crbug.com/1217708): Fallback to default values.
+    }
+
+    private boolean shouldEnterSafeMode() {
+        int crashStreak = SharedPreferencesManager.getInstance().readInt(
+                ChromePreferenceKeys.FLAGS_CRASH_STREAK_BEFORE_CACHE, 0);
+        RecordHistogram.recordExactLinearHistogram(
+                "Variations.SafeModeCachedFlags.Streak.Crashes", crashStreak, 50);
+
+        if (crashStreak >= CRASH_STREAK_TO_ENTER_SAFE_MODE) {
+            Log.e(TAG, "Enter Safe Mode for CachedFlags, crash streak is %d.", crashStreak);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private void writeSafeValues(ValuesReturned safeValuesReturned) {
+        SharedPreferencesManager prefs = SharedPreferencesManager.getInstance();
+
+        Map<String, Boolean> boolValuesToWrite = prependPrefixToKeys(
+                ChromePreferenceKeys.FLAGS_CACHED_SAFE_VALUES_BOOL, safeValuesReturned.boolMap());
+        prefs.writeBooleans(boolValuesToWrite);
+
+        Map<String, Integer> intValuesToWrite = prependPrefixToKeys(
+                ChromePreferenceKeys.FLAGS_CACHED_SAFE_VALUES_INT, safeValuesReturned.intMap());
+        prefs.writeInts(intValuesToWrite);
+
+        Map<String, Double> doubleValuesToWrite =
+                prependPrefixToKeys(ChromePreferenceKeys.FLAGS_CACHED_SAFE_VALUES_DOUBLE,
+                        safeValuesReturned.doubleMap());
+        prefs.writeDoubles(doubleValuesToWrite);
+
+        Map<String, String> stringValuesToWrite =
+                prependPrefixToKeys(ChromePreferenceKeys.FLAGS_CACHED_SAFE_VALUES_STRING,
+                        safeValuesReturned.stringMap());
+        stringValuesToWrite.put(ChromePreferenceKeys.FLAGS_CACHED_SAFE_VALUES_VERSION,
+                ChromeVersionInfo.getProductVersion());
+        prefs.writeStrings(stringValuesToWrite);
+    }
+
+    private static <T> Map<String, T> prependPrefixToKeys(KeyPrefix prefix, Map<String, T> map) {
+        Map<String, T> prefixed = new HashMap<>();
+        for (Map.Entry<String, T> kv : map.entrySet()) {
+            String safeKey = prefix.createKey(kv.getKey());
+            prefixed.put(safeKey, kv.getValue());
+        }
+        return prefixed;
+    }
+
+    @Behavior
+    int getBehaviorForTesting() {
+        return mBehavior;
+    }
+
+    void clearForTesting() {
+        mBehavior = Behavior.UNKNOWN;
+    }
+}
diff --git a/chrome/browser/new_tab_page/DIR_METADATA b/chrome/browser/new_tab_page/DIR_METADATA
new file mode 100644
index 0000000..4c751468
--- /dev/null
+++ b/chrome/browser/new_tab_page/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail: {
+  component: "UI>Browser>NewTabPage"
+}
+team_email: "ntp-dev@chromium.org"
diff --git a/chrome/browser/new_tab_page/OWNERS b/chrome/browser/new_tab_page/OWNERS
new file mode 100644
index 0000000..9802b3d
--- /dev/null
+++ b/chrome/browser/new_tab_page/OWNERS
@@ -0,0 +1 @@
+file://components/search/OWNERS
diff --git a/chrome/browser/new_tab_page/README.md b/chrome/browser/new_tab_page/README.md
new file mode 100644
index 0000000..df7aec9
--- /dev/null
+++ b/chrome/browser/new_tab_page/README.md
@@ -0,0 +1,97 @@
+WebUI New Tab Page (Desktop)
+============================
+
+On Desktop (ChromeOS, Windows, Mac, and Linux), there are multiple variants of
+the **New Tab Page** (**NTP**). The variant is selected according to the user’s
+**Default Search Engine** (**DSE**), profile, extensions and policies. This
+folders implements the backend of the first-party Google NTP. The features this
+variant supports are detailed in the following sections.
+
+# Features
+
+## One Google Bar
+
+The **One Google Bar** (**OGB**) is at the top of the NTP. The NTP
+fetches the OGB from Google servers each time it loads.
+
+## Google Logo
+
+On a day when there is no Doodle (in the user’s current country), the
+NTP shows the **Google Logo**. It comes in two variants:
+
+*   Colorful, if the user is using the default theme, or on any other
+    theme with a solid black (L=0%), white (L=100%), or gray (S=0%)
+    background color.
+*   White, if the user’s theme has a background image, or if the
+    background is a solid color, but not black, white, or gray.
+
+## Doodle
+
+The **Doodle** replaces the Google Logo on days a doodle is available. The
+doodle comes in three flavors:
+
+### Static Doodles
+
+A **Static Doodle** shows as a single static image. When clicked, it
+triggers a navigation to the Doodle’s target URL.
+
+### Animated Doodles
+
+An **Animated Doodle** initially shows a static **Call-to-Action**
+(**CTA**) image, usually with a “play” icon. When clicked, it swaps out
+the CTA image for an animated image. When clicked a second time, it
+triggers a navigation to the Doodle’s target URL.
+
+### Interactive Doodles
+
+An **Interactive Doodle** is embedded into the NTP as an `<iframe>`.
+The framed content usually contains a CTA image, but this is opaque to
+the containing NTP.
+
+The embedded Doodle can ask the containing NTP to resize the `<iframe>`
+tag to enlarge the space available for the Doodle. To do this, it sends
+a `postMessage()` call to `window.parent`. The event data supports these
+parameters:
+
+*   `cmd` (required string): must be `"resizeDoodle"`.
+*   `width` (required string): a CSS width (with units). Because the
+    Doodle cannot know the size of the outer page, values based on
+    `"100%"` (e.g. `"100%"` or `"calc(100% - 50px)"`) are recommended.
+*   `height` (required string): a CSS height (with units). Must not be a
+    percentage, but otherwise any units are OK.
+*   `duration` (optional string): a CSS duration, such as `"130ms"` or
+    `"1s"`. If `null` or absent, `"0s"` (no transition) is assumed.
+
+For example:
+
+    // Reset to initial width and height.
+    window.parent.postMessage({cmd: "resizeDoodle"});
+
+    // Transition smoothly to full-width, 350px tall.
+    window.parent.postMessage({
+        cmd: "resizeDoodle",
+        width: "100%",
+        height: "350px",
+        duration: "1s",
+    });
+
+### Realbox
+
+The **Realbox** is a search bar to make Google queries similar to the Omnibox.
+
+## Most Visited Tiles
+
+The NTP shows up to 10 **NTP Tiles** (now called shortcuts) and give
+users the ability to customize them. This includes adding new shortcuts using
+the "Add shortcut" button, deleting/editing shortcuts from the three-dot "Edit
+shortcut" menu (replaces the "X" button), and reordering via click-and-drag.
+
+### Middle-slot Promos
+
+Below the NTP tiles, there is space for a **Middle-slot Promo**. A promo is
+typically a short string, typically used for disasters (e.g. “Affected
+by the Boston Molassacre? Find a relief center near you.”) or an
+advertisement (e.g. “Try the all-new new Chromebook, with included
+toaster oven.”).
+
+Middle-slot promos are fetched from Google servers on NTP load.
diff --git a/chrome/browser/search/drive/BUILD.gn b/chrome/browser/new_tab_page/modules/drive/BUILD.gn
similarity index 100%
rename from chrome/browser/search/drive/BUILD.gn
rename to chrome/browser/new_tab_page/modules/drive/BUILD.gn
diff --git a/chrome/browser/search/drive/OWNERS b/chrome/browser/new_tab_page/modules/drive/OWNERS
similarity index 100%
rename from chrome/browser/search/drive/OWNERS
rename to chrome/browser/new_tab_page/modules/drive/OWNERS
diff --git a/chrome/browser/search/drive/drive.mojom b/chrome/browser/new_tab_page/modules/drive/drive.mojom
similarity index 100%
rename from chrome/browser/search/drive/drive.mojom
rename to chrome/browser/new_tab_page/modules/drive/drive.mojom
diff --git a/chrome/browser/search/drive/drive_handler.cc b/chrome/browser/new_tab_page/modules/drive/drive_handler.cc
similarity index 77%
rename from chrome/browser/search/drive/drive_handler.cc
rename to chrome/browser/new_tab_page/modules/drive/drive_handler.cc
index 3cde28f0..be0c3829 100644
--- a/chrome/browser/search/drive/drive_handler.cc
+++ b/chrome/browser/new_tab_page/modules/drive/drive_handler.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/search/drive/drive_handler.h"
+#include "chrome/browser/new_tab_page/modules/drive/drive_handler.h"
 
-#include "chrome/browser/search/drive/drive_service.h"
-#include "chrome/browser/search/drive/drive_service_factory.h"
+#include "chrome/browser/new_tab_page/modules/drive/drive_service.h"
+#include "chrome/browser/new_tab_page/modules/drive/drive_service_factory.h"
 
 DriveHandler::DriveHandler(
     mojo::PendingReceiver<drive::mojom::DriveHandler> handler,
diff --git a/chrome/browser/search/drive/drive_handler.h b/chrome/browser/new_tab_page/modules/drive/drive_handler.h
similarity index 74%
rename from chrome/browser/search/drive/drive_handler.h
rename to chrome/browser/new_tab_page/modules/drive/drive_handler.h
index c07045a5..705d512 100644
--- a/chrome/browser/search/drive/drive_handler.h
+++ b/chrome/browser/new_tab_page/modules/drive/drive_handler.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_SEARCH_DRIVE_DRIVE_HANDLER_H_
-#define CHROME_BROWSER_SEARCH_DRIVE_DRIVE_HANDLER_H_
+#ifndef CHROME_BROWSER_NEW_TAB_PAGE_MODULES_DRIVE_DRIVE_HANDLER_H_
+#define CHROME_BROWSER_NEW_TAB_PAGE_MODULES_DRIVE_DRIVE_HANDLER_H_
 
-#include "chrome/browser/search/drive/drive.mojom.h"
+#include "chrome/browser/new_tab_page/modules/drive/drive.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 
@@ -28,4 +28,4 @@
   Profile* profile_;
 };
 
-#endif  // CHROME_BROWSER_SEARCH_DRIVE_DRIVE_HANDLER_H_
+#endif  // CHROME_BROWSER_NEW_TAB_PAGE_MODULES_DRIVE_DRIVE_HANDLER_H_
diff --git a/chrome/browser/search/drive/drive_service.cc b/chrome/browser/new_tab_page/modules/drive/drive_service.cc
similarity index 99%
rename from chrome/browser/search/drive/drive_service.cc
rename to chrome/browser/new_tab_page/modules/drive/drive_service.cc
index 9391a42..7579791 100644
--- a/chrome/browser/search/drive/drive_service.cc
+++ b/chrome/browser/new_tab_page/modules/drive/drive_service.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/search/drive/drive_service.h"
+#include "chrome/browser/new_tab_page/modules/drive/drive_service.h"
 
 #include <algorithm>
 #include <cstdint>
diff --git a/chrome/browser/search/drive/drive_service.h b/chrome/browser/new_tab_page/modules/drive/drive_service.h
similarity index 90%
rename from chrome/browser/search/drive/drive_service.h
rename to chrome/browser/new_tab_page/modules/drive/drive_service.h
index 6c979b83..b6ba1a6 100644
--- a/chrome/browser/search/drive/drive_service.h
+++ b/chrome/browser/new_tab_page/modules/drive/drive_service.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_SEARCH_DRIVE_DRIVE_SERVICE_H_
-#define CHROME_BROWSER_SEARCH_DRIVE_DRIVE_SERVICE_H_
+#ifndef CHROME_BROWSER_NEW_TAB_PAGE_MODULES_DRIVE_DRIVE_SERVICE_H_
+#define CHROME_BROWSER_NEW_TAB_PAGE_MODULES_DRIVE_DRIVE_SERVICE_H_
 
 #include <memory>
 #include <string>
 
 #include "base/sequence_checker.h"
 #include "base/time/time.h"
-#include "chrome/browser/search/drive/drive.mojom.h"
+#include "chrome/browser/new_tab_page/modules/drive/drive.mojom.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/signin/public/identity_manager/access_token_info.h"
 #include "google_apis/gaia/google_service_auth_error.h"
@@ -73,4 +73,4 @@
   base::WeakPtrFactory<DriveService> weak_factory_{this};
 };
 
-#endif  // CHROME_BROWSER_SEARCH_DRIVE_DRIVE_SERVICE_H_
+#endif  // CHROME_BROWSER_NEW_TAB_PAGE_MODULES_DRIVE_DRIVE_SERVICE_H_
diff --git a/chrome/browser/search/drive/drive_service_factory.cc b/chrome/browser/new_tab_page/modules/drive/drive_service_factory.cc
similarity index 91%
rename from chrome/browser/search/drive/drive_service_factory.cc
rename to chrome/browser/new_tab_page/modules/drive/drive_service_factory.cc
index d45ef40..dda8494 100644
--- a/chrome/browser/search/drive/drive_service_factory.cc
+++ b/chrome/browser/new_tab_page/modules/drive/drive_service_factory.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/search/drive/drive_service_factory.h"
+#include "chrome/browser/new_tab_page/modules/drive/drive_service_factory.h"
 
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
+#include "chrome/browser/new_tab_page/modules/drive/drive_service.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/search/drive/drive_service.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "content/public/browser/browser_context.h"
diff --git a/chrome/browser/search/drive/drive_service_factory.h b/chrome/browser/new_tab_page/modules/drive/drive_service_factory.h
similarity index 78%
rename from chrome/browser/search/drive/drive_service_factory.h
rename to chrome/browser/new_tab_page/modules/drive/drive_service_factory.h
index 64c9098..0f111093 100644
--- a/chrome/browser/search/drive/drive_service_factory.h
+++ b/chrome/browser/new_tab_page/modules/drive/drive_service_factory.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_SEARCH_DRIVE_DRIVE_SERVICE_FACTORY_H_
-#define CHROME_BROWSER_SEARCH_DRIVE_DRIVE_SERVICE_FACTORY_H_
+#ifndef CHROME_BROWSER_NEW_TAB_PAGE_MODULES_DRIVE_DRIVE_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_NEW_TAB_PAGE_MODULES_DRIVE_DRIVE_SERVICE_FACTORY_H_
 
 #include "base/memory/singleton.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
@@ -27,4 +27,4 @@
       content::BrowserContext* context) const override;
 };
 
-#endif  // CHROME_BROWSER_SEARCH_DRIVE_DRIVE_SERVICE_FACTORY_H_
+#endif  // CHROME_BROWSER_NEW_TAB_PAGE_MODULES_DRIVE_DRIVE_SERVICE_FACTORY_H_
diff --git a/chrome/browser/search/drive/drive_service_unittest.cc b/chrome/browser/new_tab_page/modules/drive/drive_service_unittest.cc
similarity index 99%
rename from chrome/browser/search/drive/drive_service_unittest.cc
rename to chrome/browser/new_tab_page/modules/drive/drive_service_unittest.cc
index 2bb2491..9aae826 100644
--- a/chrome/browser/search/drive/drive_service_unittest.cc
+++ b/chrome/browser/new_tab_page/modules/drive/drive_service_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/search/drive/drive_service.h"
+#include "chrome/browser/new_tab_page/modules/drive/drive_service.h"
 #include "base/hash/hash.h"
 #include "base/json/json_reader.h"
 #include "base/test/metrics/histogram_tester.h"
diff --git a/chrome/browser/search/task_module/BUILD.gn b/chrome/browser/new_tab_page/modules/task_module/BUILD.gn
similarity index 100%
rename from chrome/browser/search/task_module/BUILD.gn
rename to chrome/browser/new_tab_page/modules/task_module/BUILD.gn
diff --git a/chrome/browser/search/task_module/OWNERS b/chrome/browser/new_tab_page/modules/task_module/OWNERS
similarity index 100%
rename from chrome/browser/search/task_module/OWNERS
rename to chrome/browser/new_tab_page/modules/task_module/OWNERS
diff --git a/chrome/browser/search/task_module/task_module.mojom b/chrome/browser/new_tab_page/modules/task_module/task_module.mojom
similarity index 100%
rename from chrome/browser/search/task_module/task_module.mojom
rename to chrome/browser/new_tab_page/modules/task_module/task_module.mojom
diff --git a/chrome/browser/search/task_module/task_module_handler.cc b/chrome/browser/new_tab_page/modules/task_module/task_module_handler.cc
similarity index 90%
rename from chrome/browser/search/task_module/task_module_handler.cc
rename to chrome/browser/new_tab_page/modules/task_module/task_module_handler.cc
index defa83d..69d457f 100644
--- a/chrome/browser/search/task_module/task_module_handler.cc
+++ b/chrome/browser/new_tab_page/modules/task_module/task_module_handler.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/search/task_module/task_module_handler.h"
+#include "chrome/browser/new_tab_page/modules/task_module/task_module_handler.h"
 
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/stringprintf.h"
+#include "chrome/browser/new_tab_page/modules/task_module/task_module_service.h"
+#include "chrome/browser/new_tab_page/modules/task_module/task_module_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/search/task_module/task_module_service.h"
-#include "chrome/browser/search/task_module/task_module_service_factory.h"
 
 namespace {
 const char* GetModuleName(task_module::mojom::TaskModuleType task_module_type) {
diff --git a/chrome/browser/search/task_module/task_module_handler.h b/chrome/browser/new_tab_page/modules/task_module/task_module_handler.h
similarity index 82%
rename from chrome/browser/search/task_module/task_module_handler.h
rename to chrome/browser/new_tab_page/modules/task_module/task_module_handler.h
index b85b7b3..77dd63cc 100644
--- a/chrome/browser/search/task_module/task_module_handler.h
+++ b/chrome/browser/new_tab_page/modules/task_module/task_module_handler.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_SEARCH_TASK_MODULE_TASK_MODULE_HANDLER_H_
-#define CHROME_BROWSER_SEARCH_TASK_MODULE_TASK_MODULE_HANDLER_H_
+#ifndef CHROME_BROWSER_NEW_TAB_PAGE_MODULES_TASK_MODULE_TASK_MODULE_HANDLER_H_
+#define CHROME_BROWSER_NEW_TAB_PAGE_MODULES_TASK_MODULE_TASK_MODULE_HANDLER_H_
 
-#include "chrome/browser/search/task_module/task_module.mojom.h"
+#include "chrome/browser/new_tab_page/modules/task_module/task_module.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 
@@ -39,4 +39,4 @@
   Profile* profile_;
 };
 
-#endif  // CHROME_BROWSER_SEARCH_TASK_MODULE_TASK_MODULE_HANDLER_H_
+#endif  // CHROME_BROWSER_NEW_TAB_PAGE_MODULES_TASK_MODULE_TASK_MODULE_HANDLER_H_
diff --git a/chrome/browser/search/task_module/task_module_service.cc b/chrome/browser/new_tab_page/modules/task_module/task_module_service.cc
similarity index 99%
rename from chrome/browser/search/task_module/task_module_service.cc
rename to chrome/browser/new_tab_page/modules/task_module/task_module_service.cc
index 8df97a27..a3b4f35 100644
--- a/chrome/browser/search/task_module/task_module_service.cc
+++ b/chrome/browser/new_tab_page/modules/task_module/task_module_service.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/search/task_module/task_module_service.h"
+#include "chrome/browser/new_tab_page/modules/task_module/task_module_service.h"
 
 #include "base/containers/contains.h"
 #include "base/containers/cxx20_erase.h"
diff --git a/chrome/browser/search/task_module/task_module_service.h b/chrome/browser/new_tab_page/modules/task_module/task_module_service.h
similarity index 89%
rename from chrome/browser/search/task_module/task_module_service.h
rename to chrome/browser/new_tab_page/modules/task_module/task_module_service.h
index 839257e..8b8594b 100644
--- a/chrome/browser/search/task_module/task_module_service.h
+++ b/chrome/browser/new_tab_page/modules/task_module/task_module_service.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_SEARCH_TASK_MODULE_TASK_MODULE_SERVICE_H_
-#define CHROME_BROWSER_SEARCH_TASK_MODULE_TASK_MODULE_SERVICE_H_
+#ifndef CHROME_BROWSER_NEW_TAB_PAGE_MODULES_TASK_MODULE_TASK_MODULE_SERVICE_H_
+#define CHROME_BROWSER_NEW_TAB_PAGE_MODULES_TASK_MODULE_TASK_MODULE_SERVICE_H_
 
 #include <list>
 #include <memory>
@@ -11,7 +11,7 @@
 #include "base/callback.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/search/task_module/task_module.mojom.h"
+#include "chrome/browser/new_tab_page/modules/task_module/task_module.mojom.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "services/data_decoder/public/cpp/data_decoder.h"
 
@@ -73,4 +73,4 @@
   base::WeakPtrFactory<TaskModuleService> weak_ptr_factory_{this};
 };
 
-#endif  // CHROME_BROWSER_SEARCH_TASK_MODULE_TASK_MODULE_SERVICE_H_
+#endif  // CHROME_BROWSER_NEW_TAB_PAGE_MODULES_TASK_MODULE_TASK_MODULE_SERVICE_H_
diff --git a/chrome/browser/search/task_module/task_module_service_factory.cc b/chrome/browser/new_tab_page/modules/task_module/task_module_service_factory.cc
similarity index 90%
rename from chrome/browser/search/task_module/task_module_service_factory.cc
rename to chrome/browser/new_tab_page/modules/task_module/task_module_service_factory.cc
index 42b963b..e85fc93 100644
--- a/chrome/browser/search/task_module/task_module_service_factory.cc
+++ b/chrome/browser/new_tab_page/modules/task_module/task_module_service_factory.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/search/task_module/task_module_service_factory.h"
+#include "chrome/browser/new_tab_page/modules/task_module/task_module_service_factory.h"
 
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
+#include "chrome/browser/new_tab_page/modules/task_module/task_module_service.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/search/task_module/task_module_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
diff --git a/chrome/browser/search/task_module/task_module_service_factory.h b/chrome/browser/new_tab_page/modules/task_module/task_module_service_factory.h
similarity index 78%
rename from chrome/browser/search/task_module/task_module_service_factory.h
rename to chrome/browser/new_tab_page/modules/task_module/task_module_service_factory.h
index c84e13a9..42bf1f4 100644
--- a/chrome/browser/search/task_module/task_module_service_factory.h
+++ b/chrome/browser/new_tab_page/modules/task_module/task_module_service_factory.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_SEARCH_TASK_MODULE_TASK_MODULE_SERVICE_FACTORY_H_
-#define CHROME_BROWSER_SEARCH_TASK_MODULE_TASK_MODULE_SERVICE_FACTORY_H_
+#ifndef CHROME_BROWSER_NEW_TAB_PAGE_MODULES_TASK_MODULE_TASK_MODULE_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_NEW_TAB_PAGE_MODULES_TASK_MODULE_TASK_MODULE_SERVICE_FACTORY_H_
 
 #include "base/memory/singleton.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
@@ -30,4 +30,4 @@
       content::BrowserContext* profile) const override;
 };
 
-#endif  // CHROME_BROWSER_SEARCH_TASK_MODULE_TASK_MODULE_SERVICE_FACTORY_H_
+#endif  // CHROME_BROWSER_NEW_TAB_PAGE_MODULES_TASK_MODULE_TASK_MODULE_SERVICE_FACTORY_H_
diff --git a/chrome/browser/search/task_module/task_module_service_unittest.cc b/chrome/browser/new_tab_page/modules/task_module/task_module_service_unittest.cc
similarity index 99%
rename from chrome/browser/search/task_module/task_module_service_unittest.cc
rename to chrome/browser/new_tab_page/modules/task_module/task_module_service_unittest.cc
index cc94db0..32cd095 100644
--- a/chrome/browser/search/task_module/task_module_service_unittest.cc
+++ b/chrome/browser/new_tab_page/modules/task_module/task_module_service_unittest.cc
@@ -13,7 +13,7 @@
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
-#include "chrome/browser/search/task_module/task_module_service.h"
+#include "chrome/browser/new_tab_page/modules/task_module/task_module_service.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/search/ntp_features.h"
 #include "components/variations/scoped_variations_ids_provider.h"
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_data.cc b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_data.cc
similarity index 93%
rename from chrome/browser/search/one_google_bar/one_google_bar_data.cc
rename to chrome/browser/new_tab_page/one_google_bar/one_google_bar_data.cc
index d08d1ba..6ed48059 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_data.cc
+++ b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_data.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/search/one_google_bar/one_google_bar_data.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_data.h"
 
 OneGoogleBarData::OneGoogleBarData() = default;
 OneGoogleBarData::OneGoogleBarData(const OneGoogleBarData&) = default;
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_data.h b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_data.h
similarity index 82%
rename from chrome/browser/search/one_google_bar/one_google_bar_data.h
rename to chrome/browser/new_tab_page/one_google_bar/one_google_bar_data.h
index 3e6889f7..47ae9b32 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_data.h
+++ b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_data.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_DATA_H_
-#define CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_DATA_H_
+#ifndef CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_DATA_H_
+#define CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_DATA_H_
 
 #include <string>
 
@@ -35,4 +35,4 @@
 bool operator==(const OneGoogleBarData& lhs, const OneGoogleBarData& rhs);
 bool operator!=(const OneGoogleBarData& lhs, const OneGoogleBarData& rhs);
 
-#endif  // CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_DATA_H_
+#endif  // CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_DATA_H_
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_loader.h b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader.h
similarity index 85%
rename from chrome/browser/search/one_google_bar/one_google_bar_loader.h
rename to chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader.h
index 285742bc..bd10f4c 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_loader.h
+++ b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_LOADER_H_
-#define CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_LOADER_H_
+#ifndef CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_LOADER_H_
+#define CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_LOADER_H_
 
 #include "base/callback_forward.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -41,4 +41,4 @@
   virtual bool SetAdditionalQueryParams(const std::string& value) = 0;
 };
 
-#endif  // CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_LOADER_H_
+#endif  // CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_LOADER_H_
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.cc b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.cc
similarity index 98%
rename from chrome/browser/search/one_google_bar/one_google_bar_loader_impl.cc
rename to chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.cc
index 5eeb8347..8065366 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.cc
+++ b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/search/one_google_bar/one_google_bar_loader_impl.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.h"
 
 #include <string>
 #include <utility>
@@ -15,7 +15,7 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_data.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_data.h"
 #include "chrome/common/chrome_content_client.h"
 #include "chrome/common/webui_url_constants.h"
 #include "components/google/core/common/google_util.h"
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.h b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.h
similarity index 84%
rename from chrome/browser/search/one_google_bar/one_google_bar_loader_impl.h
rename to chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.h
index 80898bc..3a47211 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.h
+++ b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_LOADER_IMPL_H_
-#define CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_LOADER_IMPL_H_
+#ifndef CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_LOADER_IMPL_H_
+#define CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_LOADER_IMPL_H_
 
 #include <memory>
 #include <string>
@@ -11,7 +11,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_loader.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader.h"
 #include "services/data_decoder/public/cpp/data_decoder.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -61,4 +61,4 @@
   DISALLOW_COPY_AND_ASSIGN(OneGoogleBarLoaderImpl);
 };
 
-#endif  // CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_LOADER_IMPL_H_
+#endif  // CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_LOADER_IMPL_H_
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl_unittest.cc b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl_unittest.cc
similarity index 98%
rename from chrome/browser/search/one_google_bar/one_google_bar_loader_impl_unittest.cc
rename to chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl_unittest.cc
index b39143c..b4797bd9 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl_unittest.cc
+++ b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/search/one_google_bar/one_google_bar_loader_impl.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.h"
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -13,7 +13,7 @@
 #include "base/test/test_simple_task_runner.h"
 #include "base/time/time.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_data.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_data.h"
 #include "components/signin/core/browser/signin_header_helper.h"
 #include "components/variations/scoped_variations_ids_provider.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_service.cc b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_service.cc
similarity index 95%
rename from chrome/browser/search/one_google_bar/one_google_bar_service.cc
rename to chrome/browser/new_tab_page/one_google_bar/one_google_bar_service.cc
index 2ee6209..4e339d5 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_service.cc
+++ b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_service.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/search/one_google_bar/one_google_bar_service.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_service.h"
 
 #include <utility>
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_loader.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 
 class OneGoogleBarService::SigninObserver
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_service.h b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_service.h
similarity index 82%
rename from chrome/browser/search/one_google_bar/one_google_bar_service.h
rename to chrome/browser/new_tab_page/one_google_bar/one_google_bar_service.h
index c3c93cd..b889b86 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_service.h
+++ b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_service.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_H_
-#define CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_H_
+#ifndef CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_H_
+#define CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_H_
 
 #include <memory>
 
 #include "base/observer_list.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_data.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_loader.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_service_observer.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_data.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_observer.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -75,4 +75,4 @@
   std::string language_code_;
 };
 
-#endif  // CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_H_
+#endif  // CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_H_
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_service_factory.cc b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_factory.cc
similarity index 90%
rename from chrome/browser/search/one_google_bar/one_google_bar_service_factory.cc
rename to chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_factory.cc
index f884bf9..00386388 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_service_factory.cc
+++ b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_factory.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/search/one_google_bar/one_google_bar_service_factory.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_factory.h"
 
 #include <string>
 
@@ -10,9 +10,9 @@
 #include "base/metrics/field_trial_params.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_service.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_loader_impl.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_service.h"
 #include "chrome/browser/signin/account_consistency_mode_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "components/content_settings/core/browser/cookie_settings.h"
@@ -46,7 +46,6 @@
 
 KeyedService* OneGoogleBarServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-
   Profile* profile = Profile::FromBrowserContext(context);
   signin::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(profile);
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_service_factory.h b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_factory.h
similarity index 79%
rename from chrome/browser/search/one_google_bar/one_google_bar_service_factory.h
rename to chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_factory.h
index 94ac82b6..8823225 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_service_factory.h
+++ b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_factory.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_FACTORY_H_
-#define CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_FACTORY_H_
+#ifndef CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_FACTORY_H_
 
 #include "base/macros.h"
 #include "base/memory/singleton.h"
@@ -32,4 +32,4 @@
   DISALLOW_COPY_AND_ASSIGN(OneGoogleBarServiceFactory);
 };
 
-#endif  // CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_FACTORY_H_
+#endif  // CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_FACTORY_H_
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_service_observer.h b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_observer.h
similarity index 77%
rename from chrome/browser/search/one_google_bar/one_google_bar_service_observer.h
rename to chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_observer.h
index e5f848c..4f81cc1 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_service_observer.h
+++ b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_observer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_OBSERVER_H_
-#define CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_OBSERVER_H_
+#ifndef CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_OBSERVER_H_
+#define CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_OBSERVER_H_
 
 // Observer for OneGoogleBarService.
 class OneGoogleBarServiceObserver {
@@ -21,4 +21,4 @@
   virtual void OnOneGoogleBarServiceShuttingDown() {}
 };
 
-#endif  // CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_OBSERVER_H_
+#endif  // CHROME_BROWSER_NEW_TAB_PAGE_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_SERVICE_OBSERVER_H_
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_service_unittest.cc b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_unittest.cc
similarity index 97%
rename from chrome/browser/search/one_google_bar/one_google_bar_service_unittest.cc
rename to chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_unittest.cc
index a4e6239..35ae961 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_service_unittest.cc
+++ b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/search/one_google_bar/one_google_bar_service.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_service.h"
 
 #include <memory>
 #include <utility>
@@ -10,8 +10,8 @@
 
 #include "base/macros.h"
 #include "base/test/task_environment.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_data.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_loader.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_data.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/optimization_guide/prediction/prediction_model_download_manager.cc b/chrome/browser/optimization_guide/prediction/prediction_model_download_manager.cc
index 4892192..09937d4f 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_model_download_manager.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_model_download_manager.cc
@@ -73,16 +73,10 @@
 const base::FilePath::CharType kModelFileName[] =
     FILE_PATH_LITERAL("model.tflite");
 
-bool IsRelevantFile(const base::FilePath& file_path) {
-  base::FilePath::StringType base_name_value = file_path.BaseName().value();
-  return base_name_value == kModelFileName ||
-         base_name_value == kModelInfoFileName;
-}
-
-base::FilePath GetFilePathForModelInfo(const base::FilePath& dir,
-                                       const proto::ModelInfo& model_info) {
+base::FilePath GetDirectoryForModelInfo(const base::FilePath& dir,
+                                        const proto::ModelInfo& model_info) {
   return dir.AppendASCII(base::StringPrintf(
-      "%s_%s.tflite",
+      "%s_%s",
       proto::OptimizationTarget_Name(model_info.optimization_target()).c_str(),
       base::NumberToString(model_info.version()).c_str()));
 }
@@ -269,9 +263,8 @@
   if (!unzip_paths)
     return;
 
-  unzip::UnzipWithFilter(
+  unzip::Unzip(
       unzip::LaunchUnzipper(), unzip_paths->first, unzip_paths->second,
-      base::BindRepeating(&IsRelevantFile),
       base::BindOnce(&PredictionModelDownloadManager::OnDownloadUnzipped,
                      ui_weak_ptr_factory_.GetWeakPtr(), unzip_paths->first,
                      unzip_paths->second));
@@ -338,41 +331,70 @@
             chrome::DIR_OPTIMIZATION_GUIDE_PREDICTION_MODELS,
             &(*models_dir_))) {
       RecordPredictionModelDownloadStatus(
-          PredictionModelDownloadStatus::kModelDirectoryDoesNotExist);
+          PredictionModelDownloadStatus::kOptGuideDirectoryDoesNotExist);
       models_dir_ = absl::nullopt;
       return absl::nullopt;
     }
   }
 
-  // Move model file away from temp directory.
+  // Move each packaged file away from temp directory into a new directory.
+
+  base::FilePath store_dir = GetDirectoryForModelInfo(*models_dir_, model_info);
+  if (!base::CreateDirectory(store_dir)) {
+    RecordPredictionModelDownloadStatus(
+        PredictionModelDownloadStatus::kCouldNotCreateDirectory);
+    return absl::nullopt;
+  }
+
   base::FilePath temp_model_path = unzipped_dir_path.Append(kModelFileName);
-  base::FilePath model_path = GetFilePathForModelInfo(*models_dir_, model_info);
+  // Note that the base file name is used for backwards compatibility checking
+  // in |OptimizationGuideStore::OnLoadModelsToBeUpdated|.
+  base::FilePath store_model_path =
+      store_dir.Append(optimization_guide::GetBaseFileNameForModels());
 
   proto::PredictionModel model;
   *model.mutable_model_info() = model_info;
-  SetFilePathInPredictionModel(model_path, &model);
+  SetFilePathInPredictionModel(store_model_path, &model);
 
-  base::File::Error file_error;
-  if (base::ReplaceFile(temp_model_path, model_path, &file_error)) {
-    RecordPredictionModelDownloadStatus(
-        PredictionModelDownloadStatus::kSuccess);
-    return model;
+  // Pairs are setup as `mv <first> <second>`.
+  std::vector<std::pair<base::FilePath, base::FilePath>> files_to_move;
+  files_to_move.emplace_back(std::make_pair(temp_model_path, store_model_path));
+
+  for (const proto::AdditionalModelFile& add_file :
+       model_info.additional_files()) {
+    base::FilePath temp_add_file_path =
+        unzipped_dir_path.AppendASCII(add_file.file_path());
+    base::FilePath store_add_file_path =
+        store_dir.AppendASCII(add_file.file_path());
+    files_to_move.emplace_back(
+        std::make_pair(temp_add_file_path, store_add_file_path));
   }
 
-  // ReplaceFile failed, log the error code and attempt to utilize base::Move
-  // instead as the file could be on a different storage partition.
-  UMA_HISTOGRAM_ENUMERATION(
-      "OptimizationGuide.PredictionModelDownloadManager.ReplaceFileError",
-      -file_error, -base::File::FILE_ERROR_MAX);
-  if (base::Move(temp_model_path, model_path)) {
-    RecordPredictionModelDownloadStatus(
-        PredictionModelDownloadStatus::kSuccess);
-    return model;
+  PredictionModelDownloadStatus status =
+      PredictionModelDownloadStatus::kSuccess;
+  for (const auto& move_file : files_to_move) {
+    base::File::Error file_error;
+    if (base::ReplaceFile(move_file.first, move_file.second, &file_error)) {
+      continue;
+    }
+
+    // ReplaceFile failed, log the error code and attempt to utilize base::Move
+    // instead as the file could be on a different storage partition.
+    UMA_HISTOGRAM_ENUMERATION(
+        "OptimizationGuide.PredictionModelDownloadManager.ReplaceFileError",
+        -file_error, -base::File::FILE_ERROR_MAX);
+    if (base::Move(move_file.first, move_file.second)) {
+      continue;
+    }
+
+    status = PredictionModelDownloadStatus::kFailedModelFileOtherError;
   }
 
-  RecordPredictionModelDownloadStatus(
-      PredictionModelDownloadStatus::kFailedModelFileOtherError);
-  return absl::nullopt;
+  RecordPredictionModelDownloadStatus(status);
+
+  return status == PredictionModelDownloadStatus::kSuccess
+             ? absl::make_optional(model)
+             : absl::nullopt;
 }
 
 void PredictionModelDownloadManager::NotifyModelReady(
diff --git a/chrome/browser/optimization_guide/prediction/prediction_model_download_manager_unittest.cc b/chrome/browser/optimization_guide/prediction/prediction_model_download_manager_unittest.cc
index d9cd0a7..17a36be 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_model_download_manager_unittest.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_model_download_manager_unittest.cc
@@ -554,16 +554,21 @@
       PredictionModelDownloadFileStatus::kVerifiedCrxWithGoodModelFiles);
   RunUntilIdle();
 
-  EXPECT_TRUE(observer.last_ready_model().has_value());
+  ASSERT_TRUE(observer.last_ready_model().has_value());
   EXPECT_EQ(observer.last_ready_model()->model_info().optimization_target(),
             proto::OptimizationTarget::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
   EXPECT_EQ(observer.last_ready_model()->model_info().version(), 123);
-  EXPECT_EQ(
-      GetFilePathFromPredictionModel(observer.last_ready_model().value())
-          .value()
-          .BaseName()
-          .value(),
-      FILE_PATH_LITERAL("OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD_123.tflite"));
+  EXPECT_EQ(GetFilePathFromPredictionModel(observer.last_ready_model().value())
+                .value()
+                .DirName()
+                .BaseName()
+                .value(),
+            FILE_PATH_LITERAL("OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD_123"));
+  EXPECT_EQ(GetFilePathFromPredictionModel(observer.last_ready_model().value())
+                .value()
+                .BaseName()
+                .value(),
+            FILE_PATH_LITERAL("model.tflite"));
   // Downloaded file should still be deleted.
   EXPECT_TRUE(HasPathBeenDeleted(GetFilePathForDownloadFileStatus(
       PredictionModelDownloadFileStatus::kVerifiedCrxWithGoodModelFiles)));
diff --git a/chrome/browser/predictors/loading_data_collector.h b/chrome/browser/predictors/loading_data_collector.h
index ad40ccb..e980aa9 100644
--- a/chrome/browser/predictors/loading_data_collector.h
+++ b/chrome/browser/predictors/loading_data_collector.h
@@ -11,7 +11,7 @@
 
 #include "base/containers/flat_set.h"
 #include "base/memory/weak_ptr.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "chrome/browser/predictors/loading_predictor_config.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "services/network/public/mojom/fetch_api.mojom-forward.h"
@@ -29,7 +29,7 @@
 class LoadingStatsCollector;
 struct OptimizationGuidePrediction;
 class ResourcePrefetchPredictor;
-using NavigationId = util::IdType64<content::NavigationHandle>;
+using NavigationId = base::IdType64<content::NavigationHandle>;
 
 // Data collected for origin-based prediction, for a single origin during a
 // page load (see PageRequestSummary).
diff --git a/chrome/browser/predictors/loading_predictor_tab_helper.h b/chrome/browser/predictors/loading_predictor_tab_helper.h
index 64cb6a6..931a4b5 100644
--- a/chrome/browser/predictors/loading_predictor_tab_helper.h
+++ b/chrome/browser/predictors/loading_predictor_tab_helper.h
@@ -8,7 +8,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor.h"
 #include "content/public/browser/navigation_handle_user_data.h"
 #include "content/public/browser/render_document_host_user_data.h"
@@ -27,7 +27,7 @@
 }  // namespace optimization_guide
 
 namespace predictors {
-using NavigationId = util::IdType64<content::NavigationHandle>;
+using NavigationId = base::IdType64<content::NavigationHandle>;
 
 class LoadingPredictor;
 
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index 09f475e..734bce670 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -421,6 +421,30 @@
     public static final KeyPrefix FLAGS_CACHED = new KeyPrefix("Chrome.Flags.CachedFlag.*");
 
     /**
+     * Streak of crashes before caching flags from native. This controls Safe Mode for Cached Flags.
+     */
+    public static final String FLAGS_CRASH_STREAK_BEFORE_CACHE =
+            "Chrome.Flags.CrashStreakBeforeCache";
+
+    /**
+     * Streak of crashes before caching flags from native. This controls Safe Mode for Cached Flags.
+     */
+    public static final String FLAGS_CACHED_SAFE_VALUES_VERSION = "Chrome.Flags.SafeValuesVersion";
+
+    /**
+     * Cached feature flag and field trial parameter values considered safe because they last
+     * allowed Chrome to cache new ones from native.
+     */
+    public static final KeyPrefix FLAGS_CACHED_SAFE_VALUES_BOOL =
+            new KeyPrefix("Chrome.Flags.SafeBool.*");
+    public static final KeyPrefix FLAGS_CACHED_SAFE_VALUES_DOUBLE =
+            new KeyPrefix("Chrome.Flags.SafeDouble.*");
+    public static final KeyPrefix FLAGS_CACHED_SAFE_VALUES_INT =
+            new KeyPrefix("Chrome.Flags.SafeInt.*");
+    public static final KeyPrefix FLAGS_CACHED_SAFE_VALUES_STRING =
+            new KeyPrefix("Chrome.Flags.SafeString.*");
+
+    /**
      * Cached field trial parameters generated by CachedFeatureFlags use this prefix.
      */
     public static final KeyPrefix FLAGS_FIELD_TRIAL_PARAM_CACHED =
@@ -1016,6 +1040,12 @@
                 FEED_ARTICLES_LIST_VISIBLE,
                 FIRST_RUN_SKIPPED_BY_POLICY,
                 FLAGS_CACHED.pattern(),
+                FLAGS_CACHED_SAFE_VALUES_BOOL.pattern(),
+                FLAGS_CACHED_SAFE_VALUES_DOUBLE.pattern(),
+                FLAGS_CACHED_SAFE_VALUES_INT.pattern(),
+                FLAGS_CACHED_SAFE_VALUES_STRING.pattern(),
+                FLAGS_CACHED_SAFE_VALUES_VERSION,
+                FLAGS_CRASH_STREAK_BEFORE_CACHE,
                 FLAGS_FIELD_TRIAL_PARAM_CACHED.pattern(),
                 FLAGS_LAST_CACHED_MINIMAL_BROWSER_FLAGS_TIME_MILLIS,
                 HOMEPAGE_LOCATION_POLICY,
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index f7e6a83d..084253e 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -239,11 +239,11 @@
 #include "chrome/browser/media/unified_autoplay_config.h"
 #include "chrome/browser/metrics/tab_stats/tab_stats_tracker.h"
 #include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h"
-#include "chrome/browser/search/drive/drive_service.h"
+#include "chrome/browser/new_tab_page/modules/drive/drive_service.h"
+#include "chrome/browser/new_tab_page/modules/task_module/task_module_service.h"
 #include "chrome/browser/search/instant_service.h"
 #include "chrome/browser/search/promos/promo_service.h"
 #include "chrome/browser/search/search_suggest/search_suggest_service.h"
-#include "chrome/browser/search/task_module/task_module_service.h"
 #include "chrome/browser/serial/serial_policy_allowed_ports.h"
 #include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/startup/startup_browser_creator.h"
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 30b5057..0652a43 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -1874,6 +1874,10 @@
       GetHandlersForLinkUrl();
   if (handlers.empty())
     return;
+
+  protocol_handler_registry_observation_.Observe(protocol_handler_registry_);
+  is_protocol_submenu_valid_ = true;
+
   size_t max = IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST -
                IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST;
   for (size_t i = 0; i < handlers.size() && i <= max; i++) {
@@ -2675,6 +2679,10 @@
   return handlers;
 }
 
+void RenderViewContextMenu::OnProtocolHandlerRegistryChanged() {
+  is_protocol_submenu_valid_ = false;
+}
+
 void RenderViewContextMenu::NotifyMenuShown() {
   auto* cb = GetMenuShownCallback();
   if (!cb->is_null())
@@ -2991,6 +2999,13 @@
 
 void RenderViewContextMenu::ExecProtocolHandler(int event_flags,
                                                 int handler_index) {
+  if (!is_protocol_submenu_valid_) {
+    // A protocol was changed since the time that the menu was built, so the
+    // index passed in isn't valid. The only thing that can be done now safely
+    // is to bail.
+    return;
+  }
+
   ProtocolHandlerRegistry::ProtocolHandlerList handlers =
       GetHandlersForLinkUrl();
   if (handlers.empty())
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chrome/browser/renderer_context_menu/render_view_context_menu.h
index 45aee4d..9f23d7f 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.h
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.h
@@ -11,6 +11,8 @@
 #include <vector>
 
 #include "base/files/file_path.h"
+#include "base/scoped_observation.h"
+#include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/custom_handlers/protocol_handler_registry.h"
 #include "chrome/browser/ui/browser.h"
@@ -73,7 +75,8 @@
 }  // namespace policy
 #endif
 
-class RenderViewContextMenu : public RenderViewContextMenuBase {
+class RenderViewContextMenu : public RenderViewContextMenuBase,
+                              public ProtocolHandlerRegistry::Observer {
  public:
   RenderViewContextMenu(content::RenderFrameHost* render_frame_host,
                         const content::ContextMenuParams& params);
@@ -268,6 +271,9 @@
   // on URL.
   ProtocolHandlerRegistry::ProtocolHandlerList GetHandlersForLinkUrl();
 
+  // ProtocolHandlerRegistry::Observer:
+  void OnProtocolHandlerRegistryChanged() override;
+
   // The destination URL to use if the user tries to search for or navigate to
   // a text selection.
   GURL selection_navigation_url_;
@@ -275,8 +281,19 @@
   ui::SimpleMenuModel profile_link_submenu_model_;
   std::vector<base::FilePath> profile_link_paths_;
   bool multiple_profiles_open_;
+
+  // Protocol handling:
+  // - The submenu containing the installed protocol handlers.
   ui::SimpleMenuModel protocol_handler_submenu_model_;
+  // - The registry with the protocols.
   ProtocolHandlerRegistry* protocol_handler_registry_;
+  // - The observation of the registry.
+  base::ScopedObservation<ProtocolHandlerRegistry,
+                          ProtocolHandlerRegistry::Observer>
+      protocol_handler_registry_observation_{this};
+  // - Whether or not the registered protocols have changed since the menu was
+  //   built.
+  bool is_protocol_submenu_valid_ = false;
 
   // An observer that handles spelling suggestions, "Add to dictionary", and
   // "Use enhanced spell check" items.
diff --git a/chrome/browser/resources/accessibility/accessibility.js b/chrome/browser/resources/accessibility/accessibility.js
index f60c8d5..8bc655d 100644
--- a/chrome/browser/resources/accessibility/accessibility.js
+++ b/chrome/browser/resources/accessibility/accessibility.js
@@ -73,6 +73,11 @@
   get kAXModeComplete() {
     return AXMode.kNativeAPIs | AXMode.kWebContents | AXMode.kInlineTextBoxes |
         AXMode.kScreenReader | AXMode.kHTML;
+  },
+
+  get kAXModeCompleteNoHTML() {
+    return AXMode.kNativeAPIs | AXMode.kWebContents | AXMode.kInlineTextBoxes |
+        AXMode.kScreenReader;
   }
 };
 
diff --git a/chrome/browser/resources/new_tab_page/BUILD.gn b/chrome/browser/resources/new_tab_page/BUILD.gn
index 05f7217..cf2cccf 100644
--- a/chrome/browser/resources/new_tab_page/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/BUILD.gn
@@ -332,7 +332,8 @@
   out_grd = "$target_gen_dir/drive_mojo_resources.grdp"
   input_files = [ "drive.mojom-lite.js" ]
   input_files_base_dir =
-      rebase_path("$root_gen_dir/chrome/browser/search/drive", root_build_dir)
+      rebase_path("$root_gen_dir/chrome/browser/new_tab_page/modules/drive",
+                  root_build_dir)
   resource_path_prefix = "modules/drive"
 }
 
@@ -341,8 +342,9 @@
   out_grd = "$target_gen_dir/task_module_mojo_resources.grdp"
   input_files = [ "task_module.mojom-lite.js" ]
   input_files_base_dir =
-      rebase_path("$root_gen_dir/chrome/browser/search/task_module",
-                  root_build_dir)
+      rebase_path(
+          "$root_gen_dir/chrome/browser/new_tab_page/modules/task_module",
+          root_build_dir)
   resource_path_prefix = "modules/task_module"
 }
 
@@ -426,9 +428,9 @@
   deps = [
     ":build_grd",
     "//chrome/browser/cart:mojo_bindings_js",
+    "//chrome/browser/new_tab_page/modules/drive:mojo_bindings_js",
+    "//chrome/browser/new_tab_page/modules/task_module:mojo_bindings_js",
     "//chrome/browser/promo_browser_command:mojo_bindings_js",
-    "//chrome/browser/search/drive:mojo_bindings_js",
-    "//chrome/browser/search/task_module:mojo_bindings_js",
     "//chrome/browser/ui/webui/new_tab_page:mojo_bindings_js",
     "//chrome/browser/ui/webui/realbox:mojo_bindings_js",
     "//chrome/common/search:mojo_bindings_js",
diff --git a/chrome/browser/resources/new_tab_page/modules/cart_v2/module.html b/chrome/browser/resources/new_tab_page/modules/cart_v2/module.html
index 7851aab8..020890b 100644
--- a/chrome/browser/resources/new_tab_page/modules/cart_v2/module.html
+++ b/chrome/browser/resources/new_tab_page/modules/cart_v2/module.html
@@ -35,7 +35,8 @@
 
   #cartCarousel {
     display: flex;
-    flex-direction: row;
+    flex-direction: column;
+    flex-wrap: wrap;
     overflow-x: hidden;
     padding-top: 24px;
     white-space: nowrap;
@@ -46,12 +47,6 @@
     padding-top: 16px;
   }
 
-  #carouselItems {
-    display: grid;
-    grid-auto-flow: column;
-    grid-template-rows: auto auto auto;
-  }
-
   #consentCard,
   .cart-item {
     margin: 0 15px 24px 15px;
@@ -74,7 +69,7 @@
     box-sizing: border-box;
     display: flex;
     flex-direction: column;
-    height: 116px;
+    height: 106px;
     width: 329px;
   }
 
@@ -84,7 +79,7 @@
     font-weight: 400;
     height: 40px;
     line-height: 20px;
-    margin-top: 16px;
+    margin-top: 12px;
     text-align: center;
     white-space: normal;
     width: 223px;
@@ -93,7 +88,7 @@
   #consentButtonContainer {
     display: flex;
     justify-content: center;
-    margin-top: 12px;
+    margin-top: 8px;
   }
 
   .discount-chip {
@@ -274,6 +269,10 @@
     display: inline-flex;
     width: 12px;
   }
+
+  #rightProbe {
+    margin-inline-start: 344px;
+  }
 </style>
 <div id="module">
   <ntp-module-header
@@ -292,76 +291,74 @@
   <div id="moduleContent">
     <div id="cartCarousel">
       <div id="leftProbe" class="probe"></div>
-      <div id ="carouselItems">
-        <template id="consentCardElement" is="dom-if"
-            if="[[showDiscountConsent]]">
-          <div id="consentCard">
-            <span id="consentContent">
-              [[i18n('modulesCartDiscountConsentContent')]]
-            </span>
-            <div id="consentButtonContainer">
-              <cr-button id="cancelButton"
-                  class="cancel-button" on-click="onDisallowDiscount_"
-                  on-auxclick="onDisallowDiscount_">
-                [[i18n('modulesCartDiscountConsentReject')]]
-              </cr-button>
-              <cr-button id="actionButton"
-                  class="action-button" on-click="onAllowDiscount_"
-                  on-auxclick="onAllowDiscount_">
-                [[i18n('modulesCartDiscountConsentAccept')]]
-              </cr-button>
-            </div>
+      <template id="consentCardElement" is="dom-if"
+          if="[[showDiscountConsent]]">
+        <div id="consentCard">
+          <span id="consentContent">
+            [[i18n('modulesCartDiscountConsentContent')]]
+          </span>
+          <div id="consentButtonContainer">
+            <cr-button id="cancelButton"
+                class="cancel-button" on-click="onDisallowDiscount_"
+                on-auxclick="onDisallowDiscount_">
+              [[i18n('modulesCartDiscountConsentReject')]]
+            </cr-button>
+            <cr-button id="actionButton"
+                class="action-button" on-click="onAllowDiscount_"
+                on-auxclick="onAllowDiscount_">
+              [[i18n('modulesCartDiscountConsentAccept')]]
+            </cr-button>
           </div>
-        </template>
-        <template id="cartItemRepeat" is="dom-repeat" items="[[cartItems]]">
-          <a class="cart-item" title="[[item.merchant]]"
-              href="[[item.cartUrl.url]]" on-click="onCartItemClick_"
-              on-auxclick="onCartItemClick_">
-            <div class="cart-title">
-              <div class="merchant-container">
-                <span class="merchant">[[item.merchant]]</span>
-                <cr-icon-button class="icon-more-vert"
-                    title="[[i18n('moreActions')]]"
-                    on-click="onCartMenuButtonClick_">
-                </cr-icon-button>
-              </div>
-              <template is="dom-if" if="[[item.productImageUrls.length]]">
-                <span class="item-count">
-                  &nbsp•&nbsp[[item.productImageUrls.length]]
-                </span>
-              </template>
-              <template is="dom-if" if="[[item.discountText]]">
-                <div class="discount-chip">[[item.discountText]]</div>
-              </template>
+        </div>
+      </template>
+      <template id="cartItemRepeat" is="dom-repeat" items="[[cartItems]]">
+        <a class="cart-item" title="[[item.merchant]]"
+            href="[[item.cartUrl.url]]" on-click="onCartItemClick_"
+            on-auxclick="onCartItemClick_">
+          <div class="cart-title">
+            <div class="merchant-container">
+              <span class="merchant">[[item.merchant]]</span>
+              <cr-icon-button class="icon-more-vert"
+                  title="[[i18n('moreActions')]]"
+                  on-click="onCartMenuButtonClick_">
+              </cr-icon-button>
             </div>
-            <div class="thumbnail-container">
-              <template is="dom-if"
-                  if="[[item.productImageUrls.length]]">
-                <ul class="thumbnail-list">
-                  <template is="dom-repeat"
-                      items="[[getImagesToShow_(item.productImageUrls)]]">
-                    <li>
-                      <div class="image-background">
-                        <div class="image-container">
-                          <img class="thumbnail-img" is="cr-auto-img"
-                              auto-src="[[item.url]]"></img>
-                        </div>
+            <template is="dom-if" if="[[item.productImageUrls.length]]">
+              <span class="item-count">
+                &nbsp•&nbsp[[item.productImageUrls.length]]
+              </span>
+            </template>
+            <template is="dom-if" if="[[item.discountText]]">
+              <div class="discount-chip">[[item.discountText]]</div>
+            </template>
+          </div>
+          <div class="thumbnail-container">
+            <template is="dom-if"
+                if="[[item.productImageUrls.length]]">
+              <ul class="thumbnail-list">
+                <template is="dom-repeat"
+                    items="[[getImagesToShow_(item.productImageUrls)]]">
+                  <li>
+                    <div class="image-background">
+                      <div class="image-container">
+                        <img class="thumbnail-img" is="cr-auto-img"
+                            auto-src="[[item.url]]"></img>
                       </div>
-                    </li>
-                  </template>
-                </ul>
-              </template>
-              <template id="thumbnailFallback" is="dom-if"
-                  if="[[!item.productImageUrls.length]]">
-                <div class="fallback-background">
-                  <img class="thumbnail-fallback"
-                      src="modules/cart/icons/cart_fallback.svg">
-                </div>
-              </template>
-            </div>
-          </a>
-        </template>
-      </div>
+                    </div>
+                  </li>
+                </template>
+              </ul>
+            </template>
+            <template id="thumbnailFallback" is="dom-if"
+                if="[[!item.productImageUrls.length]]">
+              <div class="fallback-background">
+                <img class="thumbnail-fallback"
+                    src="modules/cart/icons/cart_fallback.svg">
+              </div>
+            </template>
+          </div>
+        </a>
+      </template>
       <div id="rightProbe" class="probe"></div>
     </div>
     <cr-action-menu id="cartActionMenu">
diff --git a/chrome/browser/resources/new_tab_page/modules/drive/BUILD.gn b/chrome/browser/resources/new_tab_page/modules/drive/BUILD.gn
index 3abebf6..4bbd971 100644
--- a/chrome/browser/resources/new_tab_page/modules/drive/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/modules/drive/BUILD.gn
@@ -18,8 +18,7 @@
 }
 
 js_library("drive_module_proxy") {
-  deps =
-      [ "//chrome/browser/search/drive:mojo_bindings_js_library_for_compile" ]
+  deps = [ "//chrome/browser/new_tab_page/modules/drive:mojo_bindings_js_library_for_compile" ]
 }
 
 html_to_js("web_components") {
diff --git a/chrome/browser/resources/new_tab_page/modules/task_module/BUILD.gn b/chrome/browser/resources/new_tab_page/modules/task_module/BUILD.gn
index 73a4ae00..1fa3765 100644
--- a/chrome/browser/resources/new_tab_page/modules/task_module/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/modules/task_module/BUILD.gn
@@ -19,9 +19,7 @@
 }
 
 js_library("task_module_handler_proxy") {
-  deps = [
-    "//chrome/browser/search/task_module:mojo_bindings_js_library_for_compile",
-  ]
+  deps = [ "//chrome/browser/new_tab_page/modules/task_module:mojo_bindings_js_library_for_compile" ]
 }
 
 html_to_js("web_components") {
diff --git a/chrome/browser/resources/print_preview/data/print_server_store.js b/chrome/browser/resources/print_preview/data/print_server_store.js
index bce7327e..13702680 100644
--- a/chrome/browser/resources/print_preview/data/print_server_store.js
+++ b/chrome/browser/resources/print_preview/data/print_server_store.js
@@ -4,10 +4,10 @@
 
 import {NativeEventTarget as EventTarget} from 'chrome://resources/js/cr/event_target.m.js';
 
-import {DestinationStore} from './destination_store.js';
 import {NativeLayerCros, NativeLayerCrosImpl, PrintServer, PrintServersConfig} from '../native_layer_cros.js';
 
 import {PrinterType} from './destination_match.js';
+import {DestinationStore} from './destination_store.js';
 
 export class PrintServerStore extends EventTarget {
   /**
diff --git a/chrome/browser/resources/read_later/side_panel/bookmark_folder.html b/chrome/browser/resources/read_later/side_panel/bookmark_folder.html
index d9dd4205..9101f986 100644
--- a/chrome/browser/resources/read_later/side_panel/bookmark_folder.html
+++ b/chrome/browser/resources/read_later/side_panel/bookmark_folder.html
@@ -93,16 +93,16 @@
 </button>
 
 <div id="children" role="group">
-  <template is="dom-if" if="[[open_]]">
+  <template is="dom-if" if="[[open_]]" restamp>
     <template is="dom-repeat" items="[[folder.children]]">
-      <template is="dom-if" if="[[!item.url]]">
+      <template is="dom-if" if="[[!item.url]]" restamp>
         <bookmark-folder role="treeitem" folder="[[item]]"
             depth="[[childDepth_]]"
             open-folders="[[openFolders]]">
         </bookmark-folder>
       </template>
 
-      <template is="dom-if" if="[[item.url]]">
+      <template is="dom-if" if="[[item.url]]" restamp>
         <a role="treeitem" href="[[item.url]]" class="bookmark row"
             on-click="onBookmarkClick_">
           <div
diff --git a/chrome/browser/resources/read_later/side_panel/bookmark_folder.ts b/chrome/browser/resources/read_later/side_panel/bookmark_folder.ts
index 5ba089a..b4091e6 100644
--- a/chrome/browser/resources/read_later/side_panel/bookmark_folder.ts
+++ b/chrome/browser/resources/read_later/side_panel/bookmark_folder.ts
@@ -107,6 +107,44 @@
     this.open_ =
         Boolean(this.openFolders) && this.openFolders.includes(this.folder.id);
   }
+
+  private getFocusableRows_(): HTMLElement[] {
+    return Array.from(
+        this.shadowRoot!.querySelectorAll('.row, bookmark-folder'));
+  }
+
+  moveFocus(delta: -1|1): boolean {
+    const currentFocus = this.shadowRoot!.activeElement;
+    if (currentFocus instanceof BookmarkFolderElement &&
+        currentFocus.moveFocus(delta)) {
+      // If focus is already inside a nested folder, delegate the focus to the
+      // nested folder and return early if successful.
+      return true;
+    }
+
+    let moveFocusTo = null;
+    const focusableRows = this.getFocusableRows_();
+    if (currentFocus) {
+      // If focus is in this folder, move focus to the next or previous
+      // focusable row.
+      const currentFocusIndex =
+          focusableRows.indexOf(currentFocus as HTMLElement);
+      moveFocusTo = focusableRows[currentFocusIndex + delta];
+    } else {
+      // If focus is not in this folder yet, move focus to either end.
+      moveFocusTo = delta === 1 ? focusableRows[0] :
+                                  focusableRows[focusableRows.length - 1];
+    }
+
+    if (moveFocusTo instanceof BookmarkFolderElement) {
+      return moveFocusTo.moveFocus(delta);
+    } else if (moveFocusTo) {
+      moveFocusTo.focus();
+      return true;
+    } else {
+      return false;
+    }
+  }
 }
 
 customElements.define(BookmarkFolderElement.is, BookmarkFolderElement);
\ No newline at end of file
diff --git a/chrome/browser/resources/read_later/side_panel/bookmarks_list.ts b/chrome/browser/resources/read_later/side_panel/bookmarks_list.ts
index c9ac439..329975d 100644
--- a/chrome/browser/resources/read_later/side_panel/bookmarks_list.ts
+++ b/chrome/browser/resources/read_later/side_panel/bookmarks_list.ts
@@ -5,7 +5,7 @@
 import {ListPropertyUpdateBehavior} from 'chrome://resources/js/list_property_update_behavior.m.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {FOLDER_OPEN_CHANGED_EVENT} from './bookmark_folder.js';
+import {BookmarkFolderElement, FOLDER_OPEN_CHANGED_EVENT} from './bookmark_folder.js';
 import {BookmarksApiProxy} from './bookmarks_api_proxy.js';
 
 // Key for localStorage object that refers to all the open folders.
@@ -49,6 +49,7 @@
         FOLDER_OPEN_CHANGED_EVENT,
         e => this.onFolderOpenChanged_(
             e as CustomEvent<{id: string, open: boolean}>));
+    this.addEventListener('keydown', e => this.onKeydown_(e));
   }
 
   connectedCallback() {
@@ -183,6 +184,31 @@
         JSON.stringify(this.openFolders_);
   }
 
+  private onKeydown_(event: KeyboardEvent) {
+    if (!['ArrowDown', 'ArrowUp'].includes(event.key)) {
+      return;
+    }
+
+    if (!(this.shadowRoot!.activeElement instanceof BookmarkFolderElement)) {
+      // If the key event did not happen within a BookmarkFolderElement, do
+      // not do anything.
+      return;
+    }
+
+    const allFolderElements: BookmarkFolderElement[] =
+        Array.from(this.shadowRoot!.querySelectorAll('bookmark-folder'));
+
+    const delta = event.key === 'ArrowUp' ? -1 : 1;
+    let currentIndex =
+        allFolderElements.indexOf(this.shadowRoot!.activeElement);
+    let focusHasMoved = false;
+    while (!focusHasMoved) {
+      focusHasMoved = allFolderElements[currentIndex]!.moveFocus(delta);
+      currentIndex = (currentIndex + delta + allFolderElements.length) %
+          allFolderElements.length;
+    }
+  }
+
   private onMoved_(movedInfo: chrome.bookmarks.MoveInfo) {
     // Get old path and remove node from oldParent at oldIndex.
     const oldParentPath = this.findPathToId_(movedInfo.oldParentId);
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js
index fc536626..3ba10d2 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js
@@ -1077,7 +1077,7 @@
     }
     this.networkConfig_.setProperties(this.guid, config).then(response => {
       if (!response.success) {
-        console.error('Unable to set properties: ' + JSON.stringify(config));
+        console.warn('Unable to set properties: ' + JSON.stringify(config));
         // An error typically indicates invalid input; request the properties
         // to update any invalid fields.
         this.getNetworkDetails_();
@@ -1563,7 +1563,7 @@
   handleDisconnectTap_() {
     this.networkConfig_.startDisconnect(this.guid).then(response => {
       if (!response.success) {
-        console.error('Disconnect failed for: ' + this.guid);
+        console.warn('Disconnect failed for: ' + this.guid);
       }
     });
     settings.recordSettingChange();
@@ -1638,7 +1638,7 @@
   onForgetTap_() {
     this.networkConfig_.forgetNetwork(this.guid).then(response => {
       if (!response.success) {
-        console.error('Froget network failed for: ' + this.guid);
+        console.warn('Froget network failed for: ' + this.guid);
       }
       // A forgotten network no longer has a valid GUID, close the subpage.
       this.close();
@@ -1720,7 +1720,7 @@
     const valueType = typeof value;
     if (valueType !== 'string' && valueType !== 'number' &&
         valueType !== 'boolean' && !Array.isArray(value)) {
-      console.error(
+      console.warn(
           'Unexpected property change event, Key: ' + field +
           ' Value: ' + JSON.stringify(value));
       return;
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_known_networks_page.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_known_networks_page.js
index 31900de..c1eec2c 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_known_networks_page.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_known_networks_page.js
@@ -210,7 +210,7 @@
         .then(response => {
           const properties = response.result;
           if (!properties) {
-            console.error('Properties not found for: ' + this.selectedGuid_);
+            console.warn('Properties not found for: ' + this.selectedGuid_);
             return;
           }
           if (properties.priority &&
@@ -237,7 +237,7 @@
     this.networkConfig_.setProperties(this.selectedGuid_, config)
         .then(response => {
           if (!response.success) {
-            console.error(
+            console.warn(
                 'Unable to set properties for: ' + this.selectedGuid_ + ': ' +
                 JSON.stringify(config));
           }
@@ -267,7 +267,7 @@
   onForgetTap_() {
     this.networkConfig_.forgetNetwork(this.selectedGuid_).then(response => {
       if (!response.success) {
-        console.error('Froget network failed for: ' + this.selectedGuid_);
+        console.warn('Froget network failed for: ' + this.selectedGuid_);
       }
     });
 
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_detail_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_detail_view.js
index e7bb86b..a671f8d7 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_detail_view.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_detail_view.js
@@ -89,6 +89,9 @@
         return 'pwa-detail-view';
       case (AppType.kExtension):
       case (AppType.kStandaloneBrowser):
+      case (AppType.kStandaloneBrowserExtension):
+        // TODO(https://crbug.com/1225848): Figure out appropriate behavior for
+        // Lacros-hosted chrome-apps.
         return 'chrome-app-detail-view';
       case (AppType.kArc):
         return 'arc-detail-view';
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.js
index 7e66989b..a0a64ac 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.js
@@ -49,6 +49,9 @@
         return AppManagementEntryPoint.MainViewArc;
       case AppType.kExtension:
       case AppType.kStandaloneBrowser:
+      case AppType.kStandaloneBrowserExtension:
+        // TODO(https://crbug.com/1225848): Figure out appropriate behavior for
+        // Lacros-hosted chrome-apps.
         return AppManagementEntryPoint.MainViewChromeApp;
       case AppType.kWeb:
         return AppManagementEntryPoint.MainViewWebApp;
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/util.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/util.js
index 1f39fb8..442b0b0 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/util.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/util.js
@@ -228,6 +228,9 @@
         return 'AppManagement.AppDetailViews.ArcApp';
       case AppType.kExtension:
       case AppType.kStandaloneBrowser:
+      case AppType.kStandaloneBrowserExtension:
+        // TODO(https://crbug.com/1225848): Figure out appropriate behavior for
+        // Lacros-hosted chrome-apps.
         return 'AppManagement.AppDetailViews.ChromeApp';
       case AppType.kWeb:
         return 'AppManagement.AppDetailViews.WebApp';
diff --git a/chrome/browser/search/DEPS b/chrome/browser/search/DEPS
new file mode 100644
index 0000000..3a27932
--- /dev/null
+++ b/chrome/browser/search/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  # Instant and WebUI NTPs should be independent from each other.
+  "-chrome/browser/new_tab_page"
+]
diff --git a/chrome/browser/search/README.md b/chrome/browser/search/README.md
index 0f9932f..f6cef01f 100644
--- a/chrome/browser/search/README.md
+++ b/chrome/browser/search/README.md
@@ -1,150 +1,14 @@
-New Tab Page (Desktop)
-======================
+Instant New Tab Page (Desktop)
+==============================
 
 On Desktop (ChromeOS, Windows, Mac, and Linux), there are multiple
 variants of the **New Tab Page** (**NTP**). The variant is selected
-according to the user’s **Default Search Engine** (**DSE**). All
-variants are implemented as HTML/CSS/JS, but differ in where they are
-hosted.
+according to the user’s **Default Search Engine** (**DSE**), profile, extensions
+and policies. This folder implements the backend of third-party instant NTPs for
+search engines such as **Bing** and **Yandex**. The full list is in [`prepopulated_engines.json`][engines], under the key `"new_tab_url"`.
 
-*   **Google**: The **[Local NTP][local-ntp]**, with Google branding.
-
-*   **Bing**, **Yandex**: a **[Third-Party NTP][third-party-ntp]**,
-    where the NTP is hosted on third-party servers but Chrome provides
-    some of the content via <iframe> elements.
-
-*   **Other**: the **Local NTP** with no branding.
-
-As of 2017-12-05, Bing and Yandex have implemented third-party NTPs. The
-full list is in [`prepopulated_engines.json`][engines], under the key
-`"new_tab_url"`.
-
-Non-Google variants show up to 8 **NTP Tiles**. Each NTP tile represents a site
-that Chrome believes the user is likely to want to visit. On Desktop, NTP tiles
-have a title, a large icon, and an “X” button so that the user can remove tiles
-that they don’t want.
-
-Google variants show up to 10 **NTP Tiles** (now called shortcuts) and give
-users the ability to customize them. This includes adding new shortcuts using
-the "Add shortcut" button, deleting/editing shortcuts from the three-dot "Edit
-shortcut" menu (replaces the "X" button), and reordering via click-and-drag.
-
-[local-ntp]:        #local-ntp
-[third-party-ntp]:  #third_party-ntp
-[engines]:          https://chromium.googlesource.com/chromium/src/+/main/components/search_engines/prepopulated_engines.json
-
-Variants
---------
-
-### Local NTP
-
-#### Google branding
-
-##### One Google Bar
-
-The **One Google Bar** (**OGB**) is at the top of the NTP. The NTP
-fetches the OGB from Google servers each time it loads.
-
-##### Google Logo
-
-The **Google Logo** is below the OGB. By default, it is the regular
-Google logo. It can also be a **Doodle**, if a Google Doodle is running
-for a particular combination of (today’s date, user’s country, user’s
-birthday).
-
-###### No Doodle
-
-On a day when there is no Doodle (in the user’s current country), the
-NTP shows the Google logo. It comes in two variants:
-
-*   Colorful, if the user is using the default theme, or on any other
-    theme with a solid black (L=0%), white (L=100%), or gray (S=0%)
-    background color.
-*   White, if the user’s theme has a background image, or if the
-    background is a solid color, but not black, white, or gray.
-
-Also, even on days when there is a Doodle, if the user’s theme’s
-background is not solid white, new NTPs show the Google logo by default.
-In this case, an animated spinner advertises the Doodle. If the user
-clicks on the spinner, then the NTP resets to the default theme and
-shows the Doodle.
-
-###### Static Doodles
-
-A **Static Doodle** shows as a single static image. When clicked, it
-triggers a navigation to the Doodle’s target URL.
-
-###### Animated Doodles
-
-An **Animated Doodle** initially shows a static **Call-to-Action**
-(**CTA**) image, usually with a “play” icon. When clicked, it swaps out
-the CTA image for an animated image. When clicked a second time, it
-triggers a navigation to the Doodle’s target URL.
-
-###### Interactive Doodles
-
-An **Interactive Doodle** is embedded into the NTP as an `<iframe>`.
-The framed content usually contains a CTA image, but this is opaque to
-the containing NTP.
-
-The embedded Doodle can ask the containing NTP to resize the `<iframe>`
-tag to enlarge the space available for the Doodle. To do this, it sends
-a `postMessage()` call to `window.parent`. The event data supports these
-parameters:
-
-*   `cmd` (required string): must be `"resizeDoodle"`.
-*   `width` (required string): a CSS width (with units). Because the
-    Doodle cannot know the size of the outer page, values based on
-    `"100%"` (e.g. `"100%"` or `"calc(100% - 50px)"`) are recommended.
-*   `height` (required string): a CSS height (with units). Must not be a
-    percentage, but otherwise any units are OK.
-*   `duration` (optional string): a CSS duration, such as `"130ms"` or
-    `"1s"`. If `null` or absent, `"0s"` (no transition) is assumed.
-
-For example:
-
-    // Reset to initial width and height.
-    window.parent.postMessage({cmd: "resizeDoodle"});
-
-    // Transition smoothly to full-width, 350px tall.
-    window.parent.postMessage({
-        cmd: "resizeDoodle",
-        width: "100%",
-        height: "350px",
-        duration: "1s",
-    });
-
-##### Fakebox
-
-The **Fakebox** looks like a search bar, so that the NTP mimics the
-appearance of the Google homepage. It’s not actually a real search bar,
-and if the user interacts with it, the NTP moves keyboard focus and any
-text to the Omnibox and hides the Fakebox.
-
-##### Search Suggestions
-
-Above the NTP tiles there is space for search suggestions. Search suggestions
-are typically 3-4 queries recommended to logged-in users based on their previous
-search history.
-
-Search suggestions are fetched from Google servers on NTP load and cached to be
-displayed on the following NTP load.
-
-##### Middle-slot Promos
-
-Below the NTP tiles, there is space for a **Middle-slot Promo**. A promo is
-typically a short string, typically used for disasters (e.g. “Affected
-by the Boston Molassacre? Find a relief center near you.”) or an
-advertisement (e.g. “Try the all-new new Chromebook, with included
-toaster oven.”).
-
-Middle-slot promos are fetched from Google servers on NTP load.
-
-#### Non-Google Local NTP
-
-A non-Google local NTP shows only NTP tiles, with no branding. The tiles
-are centered within the page.
-
-### Third-Party NTP
-
-TODO(sfiera)
+Instant NTPs are loaded at runtime from third-party servers and gain special privileges over regular web pages. For example, instant NTPs can embed up to 8
+**NTP Tiles**. vie <iframe> elements. Each NTP tile represents a site that
+Chrome believes the user is likely to want to visit. On Desktop, NTP tiles have
+a title, a large icon, and an “X” button so that the user can remove tiles that
+they don’t want.
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java
index 1232f9c..c4af5b8 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java
@@ -7,6 +7,7 @@
 import android.app.Activity;
 import android.content.ClipData;
 import android.content.ClipboardManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.view.View;
@@ -23,7 +24,6 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.share.ChromeShareExtras;
 import org.chromium.chrome.browser.share.link_to_text.LinkToTextCoordinator;
 import org.chromium.chrome.browser.share.link_to_text.LinkToTextCoordinator.LinkGeneration;
 import org.chromium.chrome.browser.share.link_to_text.LinkToTextMetricsHelper;
@@ -58,7 +58,11 @@
 /**
  * Provides {@code PropertyModel}s of Chrome-provided sharing options.
  */
-class ChromeProvidedSharingOptionsProvider {
+public class ChromeProvidedSharingOptionsProvider {
+    // ComponentName used for Chrome share options in ShareParams.TargetChosenCallback
+    public static final ComponentName CHROME_PROVIDED_FEATURE_COMPONENT_NAME =
+            new ComponentName("CHROME", "CHROME_FEATURE");
+
     private final Activity mActivity;
     private final Supplier<Tab> mTabProvider;
     private final BottomSheetController mBottomSheetController;
@@ -70,11 +74,11 @@
     private final long mShareStartTime;
     private final List<FirstPartyOption> mOrderedFirstPartyOptions;
     private final ChromeOptionShareCallback mChromeOptionShareCallback;
-    private ScreenshotCoordinator mScreenshotCoordinator;
     private final String mUrl;
     private final ImageEditorModuleProvider mImageEditorModuleProvider;
     private final Tracker mFeatureEngagementTracker;
-    private @LinkGeneration int mLinkGenerationStatusForMetrics = LinkGeneration.MAX;
+    private final @LinkGeneration int mLinkGenerationStatusForMetrics;
+    private ScreenshotCoordinator mScreenshotCoordinator;
 
     /**
      * Constructs a new {@link ChromeProvidedSharingOptionsProvider}.
@@ -85,7 +89,6 @@
      * @param bottomSheetContent The {@link ShareSheetBottomSheetContent} for the current
      * activity.
      * @param shareParams The {@link ShareParams} for the current share.
-     * @param chromeShareExtras The {@link ChromeShareExtras} for the current share.
      * @param printTab A {@link Callback} that will print a given Tab.
      * @param shareStartTime The start time of the current share.
      * @param chromeOptionShareCallback A ChromeOptionShareCallback that can be used by
@@ -93,16 +96,14 @@
      * @param imageEditorModuleProvider Image Editor module entry point if present in the APK.
      * @param featureEngagementTracker feature engagement tracker.
      * @param url Url to share.
-     * @param shareDetailsForMetrics User action of sharing text from failed link-to-text
-     *         generation,
-     * sharing text from successful link-to-text generation, or sharing link-to-text.
+     * @param linkGenerationStatusForMetrics User action of sharing text from failed link-to-text
+     * generation, sharing text from successful link-to-text generation, or sharing link-to-text.
      */
     ChromeProvidedSharingOptionsProvider(Activity activity, Supplier<Tab> tabProvider,
             BottomSheetController bottomSheetController,
             ShareSheetBottomSheetContent bottomSheetContent, ShareParams shareParams,
-            ChromeShareExtras chromeShareExtras, Callback<Tab> printTab,
-            SettingsLauncher settingsLauncher, boolean isSyncEnabled, long shareStartTime,
-            ChromeOptionShareCallback chromeOptionShareCallback,
+            Callback<Tab> printTab, SettingsLauncher settingsLauncher, boolean isSyncEnabled,
+            long shareStartTime, ChromeOptionShareCallback chromeOptionShareCallback,
             ImageEditorModuleProvider imageEditorModuleProvider, Tracker featureEngagementTracker,
             String url, @LinkGeneration int linkGenerationStatusForMetrics) {
         mActivity = activity;
@@ -203,6 +204,7 @@
                         recordTimeToShare(mShareStartTime);
                         mBottomSheetController.hideContent(mBottomSheetContent, true);
                         mOnClickCallback.onResult(view);
+                        callTargetChosenCallback();
                     }, /*showNewBadge*/ false);
             return new FirstPartyOption(model, Arrays.asList(mContentTypesInBuilder),
                     Arrays.asList(mContentTypesToDisableFor), mDisableForMultiWindow);
@@ -297,6 +299,7 @@
                     // observer will then remove itself.
                     mBottomSheetController.addObserver(mSheetObserver);
                     mBottomSheetController.hideContent(mBottomSheetContent, true);
+                    callTargetChosenCallback();
                 }, showNewBadge);
 
         return new FirstPartyOption(propertyModel,
@@ -319,6 +322,7 @@
                     // observer will then remove itself.
                     mBottomSheetController.addObserver(mSheetObserver);
                     mBottomSheetController.hideContent(mBottomSheetContent, true);
+                    callTargetChosenCallback();
                 }, /*showNewBadge*/ false);
         return new FirstPartyOption(propertyModel,
                 Arrays.asList(ContentType.LINK_PAGE_VISIBLE, ContentType.TEXT,
@@ -451,6 +455,16 @@
                 .build();
     }
 
+    private void callTargetChosenCallback() {
+        ShareParams.TargetChosenCallback callback = mShareParams.getCallback();
+        if (callback != null) {
+            callback.onTargetChosen(CHROME_PROVIDED_FEATURE_COMPONENT_NAME);
+            // Reset callback after onTargetChosen() is called to prevent cancel() being called when
+            // the sheet is closed.
+            mShareParams.setCallback(null);
+        }
+    }
+
     static void recordTimeToShare(long shareStartTime) {
         RecordHistogram.recordMediumTimesHistogram("Sharing.SharingHubAndroid.TimeToShare",
                 System.currentTimeMillis() - shareStartTime);
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
index 479dd0d..f49afbc 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
@@ -254,8 +254,8 @@
             return new ArrayList<>();
         }
         mChromeProvidedSharingOptionsProvider = new ChromeProvidedSharingOptionsProvider(activity,
-                mTabProvider, mBottomSheetController, mBottomSheet, shareParams, chromeShareExtras,
-                mPrintTabCallback, mSettingsLauncher, mIsSyncEnabled, mShareStartTime, this,
+                mTabProvider, mBottomSheetController, mBottomSheet, shareParams, mPrintTabCallback,
+                mSettingsLauncher, mIsSyncEnabled, mShareStartTime, this,
                 mImageEditorModuleProvider, mFeatureEngagementTracker,
                 getUrlToShare(shareParams, chromeShareExtras,
                         mTabProvider.get().isInitialized() ? mTabProvider.get().getUrl().getSpec()
@@ -287,9 +287,9 @@
             PostTask.postTask(UiThreadTaskTraits.DEFAULT, callback.bind(null));
             return;
         }
-        List<PropertyModel> models = mPropertyModelBuilder.selectThirdPartyApps(mBottomSheet,
-                contentTypes, params, saveLastUsed, params.getWindow(), mShareStartTime,
-                mLinkGenerationStatusForMetrics);
+        List<PropertyModel> models =
+                mPropertyModelBuilder.selectThirdPartyApps(mBottomSheet, contentTypes, params,
+                        saveLastUsed, mShareStartTime, mLinkGenerationStatusForMetrics);
         // More...
         PropertyModel morePropertyModel = ShareSheetPropertyModelBuilder.createPropertyModel(
                 AppCompatResources.getDrawable(activity, R.drawable.sharing_more),
@@ -308,6 +308,10 @@
                         profile = Profile.fromWebContents(mTabProvider.get().getWebContents());
                     }
                     ShareHelper.showDefaultShareUi(params, profile, saveLastUsed);
+                    // Reset callback to prevent cancel() being called when the custom sheet is
+                    // closed. The callback will be called by ShareHelper on actions from the
+                    // default share UI.
+                    params.setCallback(null);
                 },
                 /*displayNew*/ false);
         models.add(morePropertyModel);
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilder.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilder.java
index b169460a..e915dec 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilder.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilder.java
@@ -25,7 +25,6 @@
 import org.chromium.chrome.browser.share.link_to_text.LinkToTextMetricsHelper;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.share.ShareParams;
-import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modelutil.PropertyModel;
 
 import java.lang.annotation.Retention;
@@ -152,10 +151,8 @@
 
     protected List<PropertyModel> selectThirdPartyApps(ShareSheetBottomSheetContent bottomSheet,
             Set<Integer> contentTypes, ShareParams params, boolean saveLastUsed,
-            WindowAndroid window, long shareStartTime,
-            @LinkGeneration int linkGenerationStatusForMetrics) {
+            long shareStartTime, @LinkGeneration int linkGenerationStatusForMetrics) {
         List<String> thirdPartyActivityNames = getThirdPartyActivityNames();
-        final ShareParams.TargetChosenCallback callback = params.getCallback();
         List<ResolveInfo> resolveInfoList =
                 getCompatibleApps(contentTypes, params.getFileContentType());
         List<ResolveInfo> thirdPartyActivities = new ArrayList<>();
@@ -190,11 +187,9 @@
         for (int i = 0; i < MAX_NUM_APPS && i < thirdPartyActivities.size(); ++i) {
             ResolveInfo info = thirdPartyActivities.get(i);
             final int logIndex = i;
-            OnClickListener onClickListener = v -> {
-                onThirdPartyAppSelected(bottomSheet, params, window, callback, saveLastUsed,
-                        info.activityInfo, logIndex, shareStartTime,
-                        linkGenerationStatusForMetrics);
-            };
+            OnClickListener onClickListener = v
+                    -> onThirdPartyAppSelected(bottomSheet, params, saveLastUsed, info.activityInfo,
+                            logIndex, shareStartTime, linkGenerationStatusForMetrics);
             PropertyModel propertyModel =
                     createPropertyModel(ShareHelper.loadIconForResolveInfo(info, mPackageManager),
                             (String) info.loadLabel(mPackageManager), onClickListener,
@@ -206,9 +201,8 @@
     }
 
     private void onThirdPartyAppSelected(ShareSheetBottomSheetContent bottomSheet,
-            ShareParams params, WindowAndroid window, ShareParams.TargetChosenCallback callback,
-            boolean saveLastUsed, ActivityInfo ai, int logIndex, long shareStartTime,
-            @LinkGeneration int linkGenerationStatusForMetrics) {
+            ShareParams params, boolean saveLastUsed, ActivityInfo ai, int logIndex,
+            long shareStartTime, @LinkGeneration int linkGenerationStatusForMetrics) {
         // Record all metrics.
         RecordUserAction.record("SharingHubAndroid.ThirdPartyAppSelected");
         RecordHistogram.recordEnumeratedHistogram(
@@ -219,8 +213,12 @@
                     linkGenerationStatusForMetrics);
         }
         ComponentName component = new ComponentName(ai.applicationInfo.packageName, ai.name);
+        ShareParams.TargetChosenCallback callback = params.getCallback();
         if (callback != null) {
             callback.onTargetChosen(component);
+            // Reset callback after onTargetChosen() is called to prevent cancel() being called when
+            // the sheet is closed.
+            params.setCallback(null);
         }
         if (saveLastUsed) {
             ShareHelper.setLastShareComponentName(mProfile, component);
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java
index 5b1fe71..3032fe0 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java
@@ -40,7 +40,6 @@
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.share.ChromeShareExtras;
 import org.chromium.chrome.browser.share.link_to_text.LinkToTextCoordinator.LinkGeneration;
 import org.chromium.chrome.browser.share.share_sheet.ShareSheetPropertyModelBuilder.ContentType;
 import org.chromium.chrome.browser.tab.Tab;
@@ -78,36 +77,31 @@
     @Rule
     public JniMocker mJniMocker = new JniMocker();
 
+    private static final String URL = "http://www.google.com/";
+
     @Mock
     private UserPrefs.Natives mUserPrefsNatives;
-
     @Mock
     private Profile mProfile;
     @Mock
     private PrefService mPrefService;
-
-    private static final String URL = "http://www.google.com/";
-
     @Mock
     private ShareSheetCoordinator mShareSheetCoordinator;
+    @Mock
+    private Supplier<Tab> mTabProvider;
+    @Mock
+    private Tab mTab;
+    @Mock
+    private BottomSheetController mBottomSheetController;
+    @Mock
+    private WebContents mWebContents;
+    @Mock
+    private Tracker mTracker;
+    @Mock
+    private ShareParams.TargetChosenCallback mTargetChosenCallback;
 
     private Activity mActivity;
     private ChromeProvidedSharingOptionsProvider mChromeProvidedSharingOptionsProvider;
-
-    @Mock
-    private Supplier<Tab> mTabProvider;
-
-    @Mock
-    private Tab mTab;
-
-    @Mock
-    private BottomSheetController mBottomSheetController;
-
-    @Mock
-    private WebContents mWebContents;
-
-    @Mock
-    private Tracker mTracker;
     private UserActionTester mActionTester;
 
     @Before
@@ -346,7 +340,6 @@
         @LinkGeneration
         int linkGenerationStatus = LinkGeneration.LINK;
 
-        String detailMetrics = "LinkGeneration.DetailsMetrics";
         setUpChromeProvidedSharingOptionsProviderTest(
                 /*printingEnabled=*/false, linkGenerationStatus);
         List<PropertyModel> propertyModels =
@@ -356,17 +349,36 @@
         assertCorrectMetrics(propertyModels, linkGenerationStatus);
     }
 
+    @Test
+    @MediumTest
+    public void getPropertyModels_onClick_callsOnTargetChosen() {
+        setUpChromeProvidedSharingOptionsProviderTest(
+                /*printingEnabled=*/false, LinkGeneration.LINK);
+
+        List<PropertyModel> propertyModels =
+                mChromeProvidedSharingOptionsProvider.getPropertyModels(
+                        ImmutableSet.of(ContentType.LINK_PAGE_VISIBLE), /*isMultiWindow=*/false);
+        View.OnClickListener onClickListener =
+                propertyModels.get(0).get(ShareSheetItemViewProperties.CLICK_LISTENER);
+
+        onClickListener.onClick(null);
+        Mockito.verify(mTargetChosenCallback, Mockito.times(1))
+                .onTargetChosen(ChromeProvidedSharingOptionsProvider
+                                        .CHROME_PROVIDED_FEATURE_COMPONENT_NAME);
+    }
+
     private void setUpChromeProvidedSharingOptionsProviderTest(
             boolean printingEnabled, @LinkGeneration int linkGenerationStatus) {
         Mockito.when(mPrefService.getBoolean(anyString())).thenReturn(printingEnabled);
 
-        ShareParams shareParams = new ShareParams.Builder(null, /*title=*/"", /*url=*/"").build();
+        ShareParams shareParams = new ShareParams.Builder(null, /*title=*/"", /*url=*/"")
+                                          .setCallback(mTargetChosenCallback)
+                                          .build();
         mChromeProvidedSharingOptionsProvider = new ChromeProvidedSharingOptionsProvider(mActivity,
                 mTabProvider, mBottomSheetController,
                 new ShareSheetBottomSheetContent(
                         mActivity, null, mShareSheetCoordinator, shareParams),
-                new ShareParams.Builder(null, "", "").build(),
-                new ChromeShareExtras.Builder().build(),
+                shareParams,
                 /*TabPrinterDelegate=*/null,
                 /*settingsLauncher=*/null,
                 /*syncState=*/false,
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java
index 02e5d78..b1d3bafb 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java
@@ -5,6 +5,8 @@
 package org.chromium.chrome.browser.share.share_sheet;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
@@ -13,6 +15,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.Activity;
+import android.view.View;
 
 import androidx.test.filters.MediumTest;
 
@@ -24,20 +27,24 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import org.chromium.base.supplier.Supplier;
 import org.chromium.base.test.BaseActivityTestRule;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeBrowserTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.share.ShareParams;
+import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.test.util.DummyUiActivity;
 
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -62,25 +69,30 @@
 
     @Mock
     private ActivityLifecycleDispatcher mLifecycleDispatcher;
-
     @Mock
     private BottomSheetController mController;
-
     @Mock
     private ShareSheetPropertyModelBuilder mPropertyModelBuilder;
-
     @Mock
-    private ShareParams mParams;
+    private ShareParams.TargetChosenCallback mTargetChosenCallback;
+    @Mock
+    private Supplier<Tab> mTabProvider;
+    @Mock
+    private WindowAndroid mWindow;
 
     private Activity mActivity;
+    private ShareParams mParams;
     private ShareSheetCoordinator mShareSheetCoordinator;
 
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
         mActivityTestRule.launchActivity(null);
         mActivity = mActivityTestRule.getActivity();
+        mParams = new ShareParams.Builder(mWindow, "title", "https://www.example.com")
+                          .setCallback(mTargetChosenCallback)
+                          .build();
 
-        MockitoAnnotations.initMocks(this);
         PropertyModel testModel1 = new PropertyModel.Builder(ShareSheetItemViewProperties.ALL_KEYS)
                                            .with(ShareSheetItemViewProperties.ICON, null)
                                            .with(ShareSheetItemViewProperties.LABEL, "testModel1")
@@ -94,12 +106,13 @@
 
         ArrayList<PropertyModel> thirdPartyPropertyModels =
                 new ArrayList<>(Arrays.asList(testModel1, testModel2));
+        when(mWindow.getActivity()).thenReturn(new WeakReference<>(mActivity));
         when(mPropertyModelBuilder.selectThirdPartyApps(
-                     any(), anySet(), any(), anyBoolean(), any(), anyLong(), anyInt()))
+                     any(), anySet(), any(), anyBoolean(), anyLong(), anyInt()))
                 .thenReturn(thirdPartyPropertyModels);
 
-        mShareSheetCoordinator = new ShareSheetCoordinator(mController, mLifecycleDispatcher, null,
-                mPropertyModelBuilder, null, null, null, false, null, null);
+        mShareSheetCoordinator = new ShareSheetCoordinator(mController, mLifecycleDispatcher,
+                mTabProvider, mPropertyModelBuilder, null, null, null, false, null, null);
     }
 
     @Test
@@ -116,8 +129,7 @@
     @Test
     @MediumTest
     public void testCreateThirdPartyPropertyModels() throws TimeoutException {
-        final AtomicReference<List<PropertyModel>> resultPropertyModels =
-                new AtomicReference<List<PropertyModel>>();
+        final AtomicReference<List<PropertyModel>> resultPropertyModels = new AtomicReference<>();
         CallbackHelper helper = new CallbackHelper();
         mShareSheetCoordinator.createThirdPartyPropertyModels(mActivity, mParams,
                 ShareSheetPropertyModelBuilder.ALL_CONTENT_TYPES_FOR_TEST,
@@ -137,4 +149,26 @@
                 mActivity.getResources().getString(R.string.sharing_more_icon_label),
                 propertyModels.get(2).get(ShareSheetItemViewProperties.LABEL));
     }
+
+    @Test
+    @MediumTest
+    public void testClickMoreRemovesCallback() throws TimeoutException {
+        final AtomicReference<List<PropertyModel>> resultPropertyModels = new AtomicReference<>();
+        CallbackHelper helper = new CallbackHelper();
+        mShareSheetCoordinator.createThirdPartyPropertyModels(mActivity, mParams,
+                ShareSheetPropertyModelBuilder.ALL_CONTENT_TYPES_FOR_TEST,
+                /*saveLastUsed=*/false, models -> {
+                    resultPropertyModels.set(models);
+                    helper.notifyCalled();
+                });
+        helper.waitForFirst();
+        List<PropertyModel> propertyModels = resultPropertyModels.get();
+
+        View.OnClickListener onClickListener =
+                propertyModels.get(2).get(ShareSheetItemViewProperties.CLICK_LISTENER);
+
+        assertNotNull("Callback should not be null before pressing More", mParams.getCallback());
+        onClickListener.onClick(null);
+        assertNull("Callback should be null after pressing More", mParams.getCallback());
+    }
 }
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilderTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilderTest.java
index f95dfbe..6ba5b6b 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilderTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilderTest.java
@@ -280,8 +280,7 @@
 
         List<PropertyModel> propertyModels = mPropertyModelBuilder.selectThirdPartyApps(null,
                 ImmutableSet.of(ContentType.LINK_PAGE_VISIBLE), shareParams, /*saveLastUsed=*/false,
-                /*WindowAndroid=*/null, /*shareStartTime=*/0,
-                /*linkGenerationStatusForMetrics=*/LinkGeneration.MAX);
+                /*shareStartTime=*/0, /*linkGenerationStatusForMetrics=*/LinkGeneration.MAX);
 
         assertEquals("Incorrect number of property models.", 2, propertyModels.size());
         assertModelsAreInTheRightOrder(
@@ -296,8 +295,7 @@
 
         List<PropertyModel> propertyModels = mPropertyModelBuilder.selectThirdPartyApps(null,
                 ImmutableSet.of(ContentType.IMAGE), shareParams, /*saveLastUsed=*/false,
-                /*WindowAndroid=*/null, /*shareStartTime=*/0,
-                /*linkGenerationStatusForMetrics=*/LinkGeneration.MAX);
+                /*shareStartTime=*/0, /*linkGenerationStatusForMetrics=*/LinkGeneration.MAX);
 
         assertEquals("Incorrect number of property models.", 2, propertyModels.size());
         assertModelsAreInTheRightOrder(
@@ -312,7 +310,7 @@
 
         List<PropertyModel> propertyModels = mPropertyModelBuilder.selectThirdPartyApps(null,
                 ImmutableSet.of(ContentType.LINK_PAGE_VISIBLE, ContentType.IMAGE), shareParams,
-                /*saveLastUsed=*/false, /*WindowAndroid=*/null, /*shareStartTime=*/0,
+                /*saveLastUsed=*/false, /*shareStartTime=*/0,
                 /*linkGenerationStatusForMetrics=*/LinkGeneration.MAX);
 
         assertEquals("Incorrect number of property models.", 4, propertyModels.size());
diff --git a/chrome/browser/sharesheet/sharesheet_service.cc b/chrome/browser/sharesheet/sharesheet_service.cc
index 98d4d68..28b910d 100644
--- a/chrome/browser/sharesheet/sharesheet_service.cc
+++ b/chrome/browser/sharesheet/sharesheet_service.cc
@@ -380,6 +380,7 @@
       case apps::mojom::AppType::kStandaloneBrowser:
       case apps::mojom::AppType::kRemote:
       case apps::mojom::AppType::kBorealis:
+      case apps::mojom::AppType::kStandaloneBrowserExtension:
       case apps::mojom::AppType::kUnknown:
         NOTREACHED();
     }
diff --git a/chrome/browser/sharing_hub/sharing_hub_features.cc b/chrome/browser/sharing_hub/sharing_hub_features.cc
index 6624f3d..10ef156d 100644
--- a/chrome/browser/sharing_hub/sharing_hub_features.cc
+++ b/chrome/browser/sharing_hub/sharing_hub_features.cc
@@ -36,12 +36,19 @@
          IsEnterprisePolicyEnabled(context);
 }
 
+bool DesktopScreenshotsFeatureEnabled() {
+  return base::FeatureList::IsEnabled(kDesktopScreenshots);
+}
+
 const base::Feature kSharingHubDesktopAppMenu{
     "SharingHubDesktopAppMenu", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kSharingHubDesktopOmnibox{
     "SharingHubDesktopOmnibox", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kDesktopScreenshots{"DesktopScreenshots",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
+
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
 void RegisterProfilePrefs(PrefRegistrySimple* registry) {
   registry->RegisterBooleanPref(prefs::kDesktopSharingHubEnabled, true);
diff --git a/chrome/browser/sharing_hub/sharing_hub_features.h b/chrome/browser/sharing_hub/sharing_hub_features.h
index 36f5e00..5c6c4bf 100644
--- a/chrome/browser/sharing_hub/sharing_hub_features.h
+++ b/chrome/browser/sharing_hub/sharing_hub_features.h
@@ -24,6 +24,12 @@
 // Windows/Mac/Linux.
 bool SharingHubOmniboxEnabled(content::BrowserContext* context);
 
+// Returns true if the desktop screenshots feature is enabled.
+// This feature is only accessed through the sharing hub. It allows the user to
+// select and capture a region of a page, and optionally to edit it with an
+// image editor before sharing.
+bool DesktopScreenshotsFeatureEnabled();
+
 // Feature flag to enable the 3-dot menu entry point for the desktop sharing
 // hub.
 extern const base::Feature kSharingHubDesktopAppMenu;
@@ -31,6 +37,10 @@
 // Feature flag to enable the omnibox entry point for the desktop sharing hub.
 extern const base::Feature kSharingHubDesktopOmnibox;
 
+// Feature flag to enable the screenshots feature, currently accessed only
+// through the sharing hub.
+extern const base::Feature kDesktopScreenshots;
+
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
 void RegisterProfilePrefs(PrefRegistrySimple* registry);
 #endif
diff --git a/chrome/browser/sharing_hub/sharing_hub_model.cc b/chrome/browser/sharing_hub/sharing_hub_model.cc
index 0204ee1..5fcbb352 100644
--- a/chrome/browser/sharing_hub/sharing_hub_model.cc
+++ b/chrome/browser/sharing_hub/sharing_hub_model.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
 #include "chrome/browser/share/core/share_targets.h"
 #include "chrome/browser/share/proto/share_target.pb.h"
+#include "chrome/browser/sharing_hub/sharing_hub_features.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/qrcode_generator/qrcode_generator_bubble_controller.h"
@@ -78,6 +79,13 @@
       {IDC_COPY_URL, l10n_util::GetStringUTF16(IDS_SHARING_HUB_COPY_LINK_LABEL),
        kCopyIcon, true});
 
+  if (DesktopScreenshotsFeatureEnabled()) {
+    first_party_action_list_.push_back(
+        {IDC_SHARING_HUB_SCREENSHOT,
+         l10n_util::GetStringUTF16(IDS_SHARING_HUB_SCREENSHOT_LABEL),
+         kSharingHubScreenshotIcon, true});
+  }
+
   first_party_action_list_.push_back(
       {IDC_SEND_TAB_TO_SELF,
        l10n_util::GetStringUTF16(IDS_CONTEXT_MENU_SEND_TAB_TO_SELF),
diff --git a/chrome/browser/sharing_hub/sharing_hub_service_factory.cc b/chrome/browser/sharing_hub/sharing_hub_service_factory.cc
index f270331..a576846b 100644
--- a/chrome/browser/sharing_hub/sharing_hub_service_factory.cc
+++ b/chrome/browser/sharing_hub/sharing_hub_service_factory.cc
@@ -34,10 +34,4 @@
   return new SharingHubService(context);
 }
 
-bool SharingHubServiceFactory::ServiceIsCreatedWithBrowserContext() const {
-  // Create this service at startup to ensure that the model exists before ui is
-  // drawn.
-
-  return true;
-}
 }  // namespace sharing_hub
diff --git a/chrome/browser/sharing_hub/sharing_hub_service_factory.h b/chrome/browser/sharing_hub/sharing_hub_service_factory.h
index 4400400..0fdb1ba 100644
--- a/chrome/browser/sharing_hub/sharing_hub_service_factory.h
+++ b/chrome/browser/sharing_hub/sharing_hub_service_factory.h
@@ -29,7 +29,6 @@
   // BrowserContextKeyedServiceFactory:
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* context) const override;
-  bool ServiceIsCreatedWithBrowserContext() const override;
 
   DISALLOW_COPY_AND_ASSIGN(SharingHubServiceFactory);
 };
diff --git a/chrome/browser/ssl/chrome_security_blocking_page_factory.cc b/chrome/browser/ssl/chrome_security_blocking_page_factory.cc
index 9d080e1..d4a92e8 100644
--- a/chrome/browser/ssl/chrome_security_blocking_page_factory.cc
+++ b/chrome/browser/ssl/chrome_security_blocking_page_factory.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/net/stub_resolver_config_reader.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/renderer_preferences_util.h"
+#include "chrome/browser/ssl/https_only_mode_controller_client.h"
 #include "chrome/browser/ssl/insecure_form/insecure_form_controller_client.h"
 #include "chrome/browser/ssl/ssl_error_controller_client.h"
 #include "chrome/browser/ssl/stateful_ssl_host_state_delegate_factory.h"
@@ -315,6 +316,19 @@
   return page;
 }
 
+std::unique_ptr<security_interstitials::HttpsOnlyModeBlockingPage>
+ChromeSecurityBlockingPageFactory::CreateHttpsOnlyModeBlockingPage(
+    content::WebContents* web_contents,
+    const GURL& request_url) {
+  std::unique_ptr<HttpsOnlyModeControllerClient> client =
+      std::make_unique<HttpsOnlyModeControllerClient>(web_contents,
+                                                      request_url);
+  auto page =
+      std::make_unique<security_interstitials::HttpsOnlyModeBlockingPage>(
+          web_contents, request_url, std::move(client));
+  return page;
+}
+
 // static
 void ChromeSecurityBlockingPageFactory::DoChromeSpecificSetup(
     SSLBlockingPageBase* page) {
diff --git a/chrome/browser/ssl/chrome_security_blocking_page_factory.h b/chrome/browser/ssl/chrome_security_blocking_page_factory.h
index 5178e6db..d79b058 100644
--- a/chrome/browser/ssl/chrome_security_blocking_page_factory.h
+++ b/chrome/browser/ssl/chrome_security_blocking_page_factory.h
@@ -11,6 +11,7 @@
 #include "components/security_interstitials/content/bad_clock_blocking_page.h"
 #include "components/security_interstitials/content/blocked_interception_blocking_page.h"
 #include "components/security_interstitials/content/captive_portal_blocking_page.h"
+#include "components/security_interstitials/content/https_only_mode_blocking_page.h"
 #include "components/security_interstitials/content/insecure_form_blocking_page.h"
 #include "components/security_interstitials/content/legacy_tls_blocking_page.h"
 #include "components/security_interstitials/content/mitm_software_blocking_page.h"
@@ -76,6 +77,9 @@
   std::unique_ptr<security_interstitials::InsecureFormBlockingPage>
   CreateInsecureFormBlockingPage(content::WebContents* web_contents,
                                  const GURL& request_url) override;
+  std::unique_ptr<security_interstitials::HttpsOnlyModeBlockingPage>
+  CreateHttpsOnlyModeBlockingPage(content::WebContents* web_contents,
+                                  const GURL& request_url) override;
 
   // Overrides the calculation of whether the app is enterprise-managed for
   // tests.
diff --git a/chrome/browser/ssl/https_defaulted_callbacks.cc b/chrome/browser/ssl/https_defaulted_callbacks.cc
index b8b1a15..d61dafa17 100644
--- a/chrome/browser/ssl/https_defaulted_callbacks.cc
+++ b/chrome/browser/ssl/https_defaulted_callbacks.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/ssl/https_defaulted_callbacks.h"
 
-#include "chrome/browser/ssl/https_only_mode_tab_storage.h"
+#include "chrome/browser/ssl/https_only_mode_tab_helper.h"
 #include "chrome/browser/ssl/typed_navigation_upgrade_throttle.h"
 #include "chrome/common/chrome_features.h"
 #include "components/omnibox/common/omnibox_features.h"
@@ -24,11 +24,11 @@
           handle);
 
   // Check HTTPS-Only Mode upgrade status.
-  auto* https_only_mode_storage =
-      HttpsOnlyModeTabStorage::GetOrCreate(handle->GetWebContents());
+  auto* https_only_mode_helper =
+      HttpsOnlyModeTabHelper::FromWebContents(handle->GetWebContents());
   bool is_https_only_mode_upgraded =
       base::FeatureList::IsEnabled(features::kHttpsOnlyMode) &&
-      https_only_mode_storage->is_navigation_upgraded();
+      https_only_mode_helper->is_navigation_upgraded();
 
   return is_upgraded_typed_navigation || is_https_only_mode_upgraded;
 }
diff --git a/chrome/browser/ssl/https_only_mode_browsertest.cc b/chrome/browser/ssl/https_only_mode_browsertest.cc
index c0fd002..290eb5eb 100644
--- a/chrome/browser/ssl/https_only_mode_browsertest.cc
+++ b/chrome/browser/ssl/https_only_mode_browsertest.cc
@@ -12,6 +12,7 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test_utils.h"
@@ -23,6 +24,7 @@
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/request_handler_util.h"
 #include "net/test/test_data_directory.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "url/url_constants.h"
 
 class HttpsOnlyModeBrowserTest : public InProcessBrowserTest {
@@ -90,6 +92,21 @@
     return prefs->GetBoolean(prefs::kHttpsOnlyModeEnabled);
   }
 
+  void ProceedThroughInterstitial(content::WebContents* tab) {
+    content::TestNavigationObserver nav_observer(tab, 1);
+    std::string javascript = "window.certificateErrorPageController.proceed();";
+    ASSERT_TRUE(content::ExecuteScript(tab, javascript));
+    nav_observer.Wait();
+  }
+
+  void DontProceedThroughInterstitial(content::WebContents* tab) {
+    content::TestNavigationObserver nav_observer(tab, 1);
+    std::string javascript =
+        "window.certificateErrorPageController.dontProceed();";
+    ASSERT_TRUE(content::ExecuteScript(tab, javascript));
+    nav_observer.Wait();
+  }
+
   net::EmbeddedTestServer* http_server() { return &http_server_; }
   net::EmbeddedTestServer* https_server() { return &https_server_; }
 
@@ -152,16 +169,13 @@
   EXPECT_FALSE(content::NavigateToURL(contents, http_url));
   EXPECT_EQ(https_url, contents->GetLastCommittedURL());
 
-  // TODO(crbug.com/1218526): Update this to use the actual interstitial once it
-  // is added.
   EXPECT_TRUE(chrome_browser_interstitials::IsInterstitialDisplayingText(
-      contents->GetMainFrame(), "HTTPS-Only Mode"));
+      contents->GetMainFrame(),
+      l10n_util::GetStringUTF8(IDS_HTTPS_ONLY_MODE_PRIMARY_PARAGRAPH)));
 }
 
-// If the user triggers an HTTPS-Only Mode interstitial for a host and then they
-// then navigate to the HTTP URL again the navigation should end up on that HTTP
-// URL and no interstitial should be shown.
-// TODO(crbug.com/1218526): Update this to be an interstitial bypass test.
+// If the user triggers an HTTPS-Only Mode interstitial for a host and then
+// clicks through the interstitial, they should end up on the HTTP URL.
 IN_PROC_BROWSER_TEST_F(HttpsOnlyModeBrowserTest,
                        InterstitialBypassed_HttpFallbackLoaded) {
   GURL http_url = http_server()->GetURL("bad-https.test", "/simple.html");
@@ -169,9 +183,12 @@
   auto* contents = browser()->tab_strip_model()->GetActiveWebContents();
   EXPECT_FALSE(content::NavigateToURL(contents, http_url));
   EXPECT_TRUE(chrome_browser_interstitials::IsInterstitialDisplayingText(
-      contents->GetMainFrame(), "HTTPS-Only Mode"));
+      contents->GetMainFrame(),
+      l10n_util::GetStringUTF8(IDS_HTTPS_ONLY_MODE_PRIMARY_PARAGRAPH)));
 
-  EXPECT_TRUE(content::NavigateToURL(contents, http_url));
+  // Proceed through the interstitial, which will add the host to the allowlist
+  // and navigate to the HTTP fallback URL.
+  ProceedThroughInterstitial(contents);
   EXPECT_EQ(http_url, contents->GetLastCommittedURL());
 }
 
@@ -186,10 +203,9 @@
   EXPECT_FALSE(content::NavigateToURL(contents, http_url));
   EXPECT_EQ(https_url, contents->GetLastCommittedURL());
 
-  // TODO(crbug.com/1218526): Update this to use the actual interstitial once it
-  // is added.
   EXPECT_TRUE(chrome_browser_interstitials::IsInterstitialDisplayingText(
-      contents->GetMainFrame(), "HTTPS-Only Mode"));
+      contents->GetMainFrame(),
+      l10n_util::GetStringUTF8(IDS_HTTPS_ONLY_MODE_PRIMARY_PARAGRAPH)));
 }
 
 // Navigations in subframes should not get upgraded by HTTPS-Only Mode. They
@@ -225,16 +241,14 @@
   auto* contents = browser()->tab_strip_model()->GetActiveWebContents();
   EXPECT_FALSE(content::NavigateToURL(contents, parent_url));
 
-  // TODO(crbug.com/1218526): Update this to use the actual interstitial once it
-  // is added.
   EXPECT_TRUE(chrome_browser_interstitials::IsInterstitialDisplayingText(
-      contents->GetMainFrame(), "HTTPS-Only Mode"));
-  // For now, the host is added to the allowlist automatically on showing the
-  // error page. Repeat the navigation and it will succeed.
-  EXPECT_TRUE(content::NavigateToURL(contents, parent_url));
+      contents->GetMainFrame(),
+      l10n_util::GetStringUTF8(IDS_HTTPS_ONLY_MODE_PRIMARY_PARAGRAPH)));
+  // Proceeding through the interstitial will add the hostname to the allowlist.
+  ProceedThroughInterstitial(contents);
 
   // Navigate the iframe to `iframe_url`. It should successfully navigate and
-  // not get upgraded to HTTPS.
+  // not get upgraded to HTTPS as the hostname is now in the allowlist.
   content::TestNavigationObserver nav_observer(contents, 1);
   EXPECT_TRUE(content::NavigateIframeToURL(contents, "test", iframe_url));
   nav_observer.Wait();
@@ -244,7 +258,9 @@
 // Tests that a navigation to the HTTP version of a site with an HTTPS version
 // that is slow to respond gets upgraded to HTTPS but times out and shows the
 // HTTPS-Only Mode interstitial.
-IN_PROC_BROWSER_TEST_F(HttpsOnlyModeBrowserTest, SlowHttps_ShouldInterstitial) {
+// TODO(crbug.com/1218526): Re-enable once fast-timeout is working.
+IN_PROC_BROWSER_TEST_F(HttpsOnlyModeBrowserTest,
+                       DISABLED_SlowHttps_ShouldInterstitial) {
   // Set timeout to zero so that HTTPS upgrades immediately timeout.
   HttpsOnlyModeNavigationThrottle::set_timeout_for_testing(0);
 
@@ -252,10 +268,9 @@
   auto* contents = browser()->tab_strip_model()->GetActiveWebContents();
   EXPECT_FALSE(content::NavigateToURL(contents, url));
 
-  // TODO(crbug.com/1218526): Update this to use the actual interstitial once it
-  // is added.
   EXPECT_TRUE(chrome_browser_interstitials::IsInterstitialDisplayingText(
-      contents->GetMainFrame(), "HTTPS-Only Mode"));
+      contents->GetMainFrame(),
+      l10n_util::GetStringUTF8(IDS_HTTPS_ONLY_MODE_PRIMARY_PARAGRAPH)));
 }
 
 // Tests that an HTTP POST form navigation to "bar.com" from an HTTP page on
@@ -276,14 +291,12 @@
   auto* contents = browser()->tab_strip_model()->GetActiveWebContents();
   EXPECT_FALSE(content::NavigateToURL(
       contents, http_server()->GetURL("bad-https.test", replacement_path)));
-  // TODO(crbug.com/1218526): Update this to use the actual interstitial once it
-  // is added.
   EXPECT_TRUE(chrome_browser_interstitials::IsInterstitialDisplayingText(
-      contents->GetMainFrame(), "HTTPS-Only Mode"));
+      contents->GetMainFrame(),
+      l10n_util::GetStringUTF8(IDS_HTTPS_ONLY_MODE_PRIMARY_PARAGRAPH)));
 
-  // Navigate again to bypass the interstitial.
-  EXPECT_TRUE(content::NavigateToURL(
-      contents, http_server()->GetURL("bad-https.test", replacement_path)));
+  // Proceed through the interstitial to add the hostname to the allowlist.
+  ProceedThroughInterstitial(contents);
 
   // Submit the form and wait for the navigation to complete.
   content::TestNavigationObserver nav_observer(contents, 1);
@@ -347,11 +360,9 @@
   GURL url = downgrading_server.GetURL("foo.com", "/");
   auto* contents = browser()->tab_strip_model()->GetActiveWebContents();
   EXPECT_FALSE(content::NavigateToURL(contents, url));
-
-  // TODO(crbug.com/1218526): Update this to use the actual interstitial once it
-  // is added.
   EXPECT_TRUE(chrome_browser_interstitials::IsInterstitialDisplayingText(
-      contents->GetMainFrame(), "HTTPS-Only Mode"));
+      contents->GetMainFrame(),
+      l10n_util::GetStringUTF8(IDS_HTTPS_ONLY_MODE_PRIMARY_PARAGRAPH)));
 }
 
 // Tests that (if no testing port is specified), the upgraded HTTPS version of
@@ -374,8 +385,8 @@
   EXPECT_FALSE(nav_observer.last_navigation_succeeded());
   EXPECT_EQ(url::kHttpsScheme, contents->GetLastCommittedURL().scheme());
   EXPECT_EQ(443, contents->GetLastCommittedURL().EffectiveIntPort());
-  // TODO(crbug.com/1218526): Update this to use the actual interstitial once it
-  // is added.
+
   EXPECT_TRUE(chrome_browser_interstitials::IsInterstitialDisplayingText(
-      contents->GetMainFrame(), "HTTPS-Only Mode"));
+      contents->GetMainFrame(),
+      l10n_util::GetStringUTF8(IDS_HTTPS_ONLY_MODE_PRIMARY_PARAGRAPH)));
 }
diff --git a/chrome/browser/ssl/https_only_mode_controller_client.cc b/chrome/browser/ssl/https_only_mode_controller_client.cc
new file mode 100644
index 0000000..9b4d78f
--- /dev/null
+++ b/chrome/browser/ssl/https_only_mode_controller_client.cc
@@ -0,0 +1,62 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ssl/https_only_mode_controller_client.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/interstitials/chrome_settings_page_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ssl/https_only_mode_tab_helper.h"
+#include "chrome/browser/ssl/stateful_ssl_host_state_delegate_factory.h"
+#include "chrome/common/webui_url_constants.h"
+#include "components/security_interstitials/content/settings_page_helper.h"
+#include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h"
+#include "content/public/browser/web_contents.h"
+
+// static
+std::unique_ptr<security_interstitials::MetricsHelper>
+HttpsOnlyModeControllerClient::GetMetricsHelper(const GURL& url) {
+  security_interstitials::MetricsHelper::ReportDetails settings;
+  settings.metric_prefix = "https_only_mode";
+  return std::make_unique<security_interstitials::MetricsHelper>(url, settings,
+                                                                 nullptr);
+}
+
+HttpsOnlyModeControllerClient::HttpsOnlyModeControllerClient(
+    content::WebContents* web_contents,
+    const GURL& request_url)
+    : SecurityInterstitialControllerClient(
+          web_contents,
+          GetMetricsHelper(request_url),
+          Profile::FromBrowserContext(web_contents->GetBrowserContext())
+              ->GetPrefs(),
+          g_browser_process->GetApplicationLocale(),
+          GURL(chrome::kChromeUINewTabURL),
+          /*settings_page_helper=*/nullptr),
+      web_contents_(web_contents),
+      request_url_(request_url) {}
+
+HttpsOnlyModeControllerClient::~HttpsOnlyModeControllerClient() = default;
+
+void HttpsOnlyModeControllerClient::GoBack() {
+  SecurityInterstitialControllerClient::GoBackAfterNavigationCommitted();
+}
+
+void HttpsOnlyModeControllerClient::Proceed() {
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents_->GetBrowserContext());
+  StatefulSSLHostStateDelegate* state =
+      static_cast<StatefulSSLHostStateDelegate*>(
+          profile->GetSSLHostStateDelegate());
+  // StatefulSSLHostStateDelegate can be null during tests.
+  if (state) {
+    state->AllowHttpForHost(request_url_.host());
+  }
+  auto* tab_helper = HttpsOnlyModeTabHelper::FromWebContents(web_contents_);
+  tab_helper->set_is_navigation_fallback(true);
+  web_contents_->GetController().Reload(content::ReloadType::NORMAL, false);
+  // The failed https navigation will remain as a forward entry, so it needs to
+  // be removed.
+  web_contents_->GetController().PruneForwardEntries();
+}
diff --git a/chrome/browser/ssl/https_only_mode_controller_client.h b/chrome/browser/ssl/https_only_mode_controller_client.h
new file mode 100644
index 0000000..d07a431
--- /dev/null
+++ b/chrome/browser/ssl/https_only_mode_controller_client.h
@@ -0,0 +1,37 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SSL_HTTPS_ONLY_MODE_CONTROLLER_CLIENT_H_
+#define CHROME_BROWSER_SSL_HTTPS_ONLY_MODE_CONTROLLER_CLIENT_H_
+
+#include "components/security_interstitials/content/content_metrics_helper.h"
+#include "components/security_interstitials/content/security_interstitial_controller_client.h"
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+class HttpsOnlyModeControllerClient
+    : public security_interstitials::SecurityInterstitialControllerClient {
+ public:
+  static std::unique_ptr<security_interstitials::MetricsHelper>
+  GetMetricsHelper(const GURL& url);
+
+  HttpsOnlyModeControllerClient(content::WebContents* web_contents,
+                                const GURL& request_url);
+  HttpsOnlyModeControllerClient(const HttpsOnlyModeControllerClient&) = delete;
+  HttpsOnlyModeControllerClient& operator=(
+      const HttpsOnlyModeControllerClient&) = delete;
+  ~HttpsOnlyModeControllerClient() override;
+
+  // security_interstitials::ControllerClient:
+  void GoBack() override;
+  void Proceed() override;
+
+ private:
+  content::WebContents* web_contents_;
+  const GURL request_url_;
+};
+
+#endif  // CHROME_BROWSER_SSL_HTTPS_ONLY_MODE_CONTROLLER_CLIENT_H_
diff --git a/chrome/browser/ssl/https_only_mode_navigation_throttle.cc b/chrome/browser/ssl/https_only_mode_navigation_throttle.cc
index e2fea67..dfcee2b 100644
--- a/chrome/browser/ssl/https_only_mode_navigation_throttle.cc
+++ b/chrome/browser/ssl/https_only_mode_navigation_throttle.cc
@@ -7,10 +7,11 @@
 #include "base/feature_list.h"
 #include "base/task/post_task.h"
 #include "base/time/time.h"
-#include "chrome/browser/ssl/https_only_mode_tab_storage.h"
+#include "chrome/browser/ssl/https_only_mode_tab_helper.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents.h"
@@ -25,6 +26,7 @@
 std::unique_ptr<HttpsOnlyModeNavigationThrottle>
 HttpsOnlyModeNavigationThrottle::MaybeCreateThrottleFor(
     content::NavigationHandle* handle,
+    std::unique_ptr<SecurityBlockingPageFactory> blocking_page_factory,
     PrefService* prefs) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
@@ -40,12 +42,15 @@
     return nullptr;
   }
 
-  return std::make_unique<HttpsOnlyModeNavigationThrottle>(handle);
+  return std::make_unique<HttpsOnlyModeNavigationThrottle>(
+      handle, std::move(blocking_page_factory));
 }
 
 HttpsOnlyModeNavigationThrottle::HttpsOnlyModeNavigationThrottle(
-    content::NavigationHandle* handle)
-    : content::NavigationThrottle(handle) {}
+    content::NavigationHandle* handle,
+    std::unique_ptr<SecurityBlockingPageFactory> blocking_page_factory)
+    : content::NavigationThrottle(handle),
+      blocking_page_factory_(std::move(blocking_page_factory)) {}
 
 HttpsOnlyModeNavigationThrottle::~HttpsOnlyModeNavigationThrottle() = default;
 
@@ -54,9 +59,9 @@
   // If the navigation was upgraded by the Interceptor, start the timeout timer.
   // Which navigations to upgrade is determined by the Interceptor not the
   // Throttle.
-  auto* tab_storage = HttpsOnlyModeTabStorage::GetOrCreate(
-      navigation_handle()->GetWebContents());
-  if (tab_storage->is_navigation_upgraded()) {
+  auto* tab_helper = HttpsOnlyModeTabHelper::FromWebContents(
+                        navigation_handle()->GetWebContents());
+  if (tab_helper->is_navigation_upgraded()) {
     timer_.Start(FROM_HERE, g_fallback_delay, this,
                  &HttpsOnlyModeNavigationThrottle::OnHttpsLoadTimeout);
   }
@@ -71,30 +76,33 @@
   // interstitial in case of SSL errors or other net errors.
   timer_.Stop();
 
+  auto* handle = navigation_handle();
+
   // If there was no certificate error, SSLInfo will be empty.
-  const net::SSLInfo info =
-      navigation_handle()->GetSSLInfo().value_or(net::SSLInfo());
+  const net::SSLInfo info = handle->GetSSLInfo().value_or(net::SSLInfo());
   int cert_status = info.cert_status;
   if (!net::IsCertStatusError(cert_status) &&
-      navigation_handle()->GetNetErrorCode() == net::OK) {
+      handle->GetNetErrorCode() == net::OK) {
     // Don't fallback.
     return content::NavigationThrottle::PROCEED;
   }
 
   // Only show the interstitial if the Interceptor attempted to upgrade the
   // navigation.
-  auto* tab_storage = HttpsOnlyModeTabStorage::GetOrCreate(
-      navigation_handle()->GetWebContents());
-  if (tab_storage->is_navigation_upgraded()) {
-    // For now this just adds the host to a basic tab-scoped allowlist and shows
-    // a placeholder error string.
-    // TODO(crbug.com/1218526): Replace this placeholder with the
-    // actual blocking page HTML.
-    tab_storage->AddHostToAllowlist(navigation_handle()->GetURL().host());
+  auto* contents = handle->GetWebContents();
+  auto* tab_helper = HttpsOnlyModeTabHelper::FromWebContents(contents);
+  if (tab_helper->is_navigation_upgraded()) {
+    std::unique_ptr<security_interstitials::HttpsOnlyModeBlockingPage>
+        blocking_page = blocking_page_factory_->CreateHttpsOnlyModeBlockingPage(
+            contents, handle->GetURL());
+    std::string interstitial_html = blocking_page->GetHTMLContents();
+    security_interstitials::SecurityInterstitialTabHelper::
+        AssociateBlockingPage(handle->GetWebContents(),
+                              handle->GetNavigationId(),
+                              std::move(blocking_page));
     return content::NavigationThrottle::ThrottleCheckResult(
         content::NavigationThrottle::CANCEL, net::ERR_BLOCKED_BY_CLIENT,
-        std::string(
-            "<html><body>Blocked due to HTTPS-Only Mode</body></html>"));
+        interstitial_html);
   }
 
   return content::NavigationThrottle::PROCEED;
@@ -114,9 +122,9 @@
   // If the timer is not yet started and this is now an upgraded navigation,
   // then start the timer here. This can happen if the initial request is to
   // HTTPS but then redirects to HTTP.
-  auto* tab_storage = HttpsOnlyModeTabStorage::GetOrCreate(
-      navigation_handle()->GetWebContents());
-  if (tab_storage->is_navigation_upgraded() && !timer_.IsRunning()) {
+  auto* tab_helper = HttpsOnlyModeTabHelper::FromWebContents(
+                        navigation_handle()->GetWebContents());
+  if (tab_helper->is_navigation_upgraded() && !timer_.IsRunning()) {
     timer_.Start(FROM_HERE, g_fallback_delay, this,
                  &HttpsOnlyModeNavigationThrottle::OnHttpsLoadTimeout);
   }
@@ -130,9 +138,9 @@
   timer_.Stop();
 
   // Clear the status for this navigation as it will successfully commit.
-  auto* tab_storage = HttpsOnlyModeTabStorage::GetOrCreate(
-      navigation_handle()->GetWebContents());
-  tab_storage->set_is_navigation_upgraded(false);
+  auto* tab_helper = HttpsOnlyModeTabHelper::FromWebContents(
+                        navigation_handle()->GetWebContents());
+  tab_helper->set_is_navigation_upgraded(false);
 
   return content::NavigationThrottle::PROCEED;
 }
@@ -148,18 +156,5 @@
 }
 
 void HttpsOnlyModeNavigationThrottle::OnHttpsLoadTimeout() {
-  // Stop the current navigation and replace it with an error page.
-  auto* web_contents = navigation_handle()->GetWebContents();
-  auto* tab_storage = HttpsOnlyModeTabStorage::GetOrCreate(web_contents);
-  tab_storage->set_is_navigation_upgraded(false);
-
-  // For now this just adds the host to a basic tab-scoped allowlist and shows
-  // a placeholder error string.
-  // TODO(crbug.com/crbug.com/1218526): Replace this placeholder with the
-  // actual blocking page HTML.
-  tab_storage->AddHostToAllowlist(navigation_handle()->GetURL().host());
-  web_contents->GetController().LoadPostCommitErrorPage(
-      web_contents->GetMainFrame(), navigation_handle()->GetURL(),
-      std::string("<html><body>Blocked due to HTTPS-Only Mode</body></html>"),
-      net::ERR_BLOCKED_BY_CLIENT);
+  // TODO(crbug.com/1226232): Trigger WillFailResponse.
 }
diff --git a/chrome/browser/ssl/https_only_mode_navigation_throttle.h b/chrome/browser/ssl/https_only_mode_navigation_throttle.h
index b015810..2e34f2c 100644
--- a/chrome/browser/ssl/https_only_mode_navigation_throttle.h
+++ b/chrome/browser/ssl/https_only_mode_navigation_throttle.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/timer/timer.h"
+#include "components/security_interstitials/content/security_blocking_page_factory.h"
 #include "content/public/browser/navigation_throttle.h"
 #include "url/gurl.h"
 
@@ -20,7 +21,7 @@
 //
 // Metadata about the navigation state (as it pertains to HTTPS-Only Mode)
 // shared between HttpsOnlyModeUpgradeInterceptor and
-// HttpsOnlyModeNavigationThrottle is stored in an HttpsOnlyModeTabStorage set
+// HttpsOnlyModeNavigationThrottle is stored in an HttpsOnlyModeTabHelper set
 // as user-data on the WebContents in which the navigation occurs. (Such
 // metadata might ordinarily be added to ChromeNavigationUIData, but the
 // Interceptor only receives a clone of the data, so it can't be used as a
@@ -28,9 +29,14 @@
 class HttpsOnlyModeNavigationThrottle : public content::NavigationThrottle {
  public:
   static std::unique_ptr<HttpsOnlyModeNavigationThrottle>
-  MaybeCreateThrottleFor(content::NavigationHandle* handle, PrefService* prefs);
+  MaybeCreateThrottleFor(
+      content::NavigationHandle* handle,
+      std::unique_ptr<SecurityBlockingPageFactory> blocking_page_factory,
+      PrefService* prefs);
 
-  explicit HttpsOnlyModeNavigationThrottle(content::NavigationHandle* handle);
+  HttpsOnlyModeNavigationThrottle(
+      content::NavigationHandle* handle,
+      std::unique_ptr<SecurityBlockingPageFactory> blocking_page_factory);
   ~HttpsOnlyModeNavigationThrottle() override;
 
   HttpsOnlyModeNavigationThrottle(const HttpsOnlyModeNavigationThrottle&) =
@@ -53,6 +59,8 @@
   void OnHttpsLoadTimeout();
 
   base::OneShotTimer timer_;
+
+  std::unique_ptr<SecurityBlockingPageFactory> blocking_page_factory_;
 };
 
 #endif  // CHROME_BROWSER_SSL_HTTPS_ONLY_MODE_NAVIGATION_THROTTLE_H_
diff --git a/chrome/browser/ssl/https_only_mode_tab_helper.cc b/chrome/browser/ssl/https_only_mode_tab_helper.cc
new file mode 100644
index 0000000..4645440
--- /dev/null
+++ b/chrome/browser/ssl/https_only_mode_tab_helper.cc
@@ -0,0 +1,33 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ssl/https_only_mode_tab_helper.h"
+
+#include "components/security_interstitials/content/https_only_mode_blocking_page.h"
+#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
+#include "content/public/browser/navigation_handle.h"
+
+HttpsOnlyModeTabHelper::~HttpsOnlyModeTabHelper() = default;
+
+void HttpsOnlyModeTabHelper::ReadyToCommitNavigation(
+    content::NavigationHandle* navigation_handle) {
+  if (is_timer_interstitial()) {
+    set_is_timer_interstitial(false);
+    std::unique_ptr<security_interstitials::HttpsOnlyModeBlockingPage>
+        blocking_page = factory_->CreateHttpsOnlyModeBlockingPage(
+            navigation_handle->GetWebContents(), fallback_url());
+    security_interstitials::SecurityInterstitialTabHelper::
+        AssociateBlockingPage(navigation_handle->GetWebContents(),
+                              navigation_handle->GetNavigationId(),
+                              std::move(blocking_page));
+  }
+}
+
+HttpsOnlyModeTabHelper::HttpsOnlyModeTabHelper(
+    content::WebContents* web_contents)
+    : WebContentsObserver(web_contents) {
+  factory_ = std::make_unique<ChromeSecurityBlockingPageFactory>();
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(HttpsOnlyModeTabHelper)
diff --git a/chrome/browser/ssl/https_only_mode_tab_helper.h b/chrome/browser/ssl/https_only_mode_tab_helper.h
new file mode 100644
index 0000000..ed684c2
--- /dev/null
+++ b/chrome/browser/ssl/https_only_mode_tab_helper.h
@@ -0,0 +1,80 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SSL_HTTPS_ONLY_MODE_TAB_HELPER_H_
+#define CHROME_BROWSER_SSL_HTTPS_ONLY_MODE_TAB_HELPER_H_
+
+#include "chrome/browser/ssl/chrome_security_blocking_page_factory.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace content {
+class NavigationHandle;
+class WebContents;
+}  // namespace content
+
+// A short-lived, per-tab helper for tracking HTTPS-Only Mode data about the
+// navigation and for creating the blocking page for the early-timeout code
+// path.
+class HttpsOnlyModeTabHelper
+    : public content::WebContentsObserver,
+      public content::WebContentsUserData<HttpsOnlyModeTabHelper> {
+ public:
+  HttpsOnlyModeTabHelper(const HttpsOnlyModeTabHelper&) = delete;
+  HttpsOnlyModeTabHelper& operator=(const HttpsOnlyModeTabHelper&) = delete;
+  ~HttpsOnlyModeTabHelper() override;
+
+  // content::WebContentsObserver:
+  void ReadyToCommitNavigation(
+      content::NavigationHandle* navigation_handle) override;
+
+  // HTTPS-Only Mode metadata getters and setters:
+  void set_is_navigation_upgraded(bool upgraded) {
+    is_navigation_upgraded_ = upgraded;
+  }
+  bool is_navigation_upgraded() const { return is_navigation_upgraded_; }
+
+  void set_is_navigation_fallback(bool fallback) {
+    is_navigation_fallback_ = fallback;
+  }
+  bool is_navigation_fallback() const { return is_navigation_fallback_; }
+
+  void set_is_timer_interstitial(bool fallback) {
+    is_timer_interstitial_ = fallback;
+  }
+  bool is_timer_interstitial() const { return is_timer_interstitial_; }
+
+  void set_fallback_url(const GURL& fallback_url) {
+    fallback_url_ = fallback_url;
+  }
+  GURL fallback_url() const { return fallback_url_; }
+
+ private:
+  explicit HttpsOnlyModeTabHelper(content::WebContents* web_contents);
+  friend class content::WebContentsUserData<HttpsOnlyModeTabHelper>;
+
+  std::unique_ptr<ChromeSecurityBlockingPageFactory> factory_;
+
+  // TODO(crbug.com/1218526): Track upgrade status per-navigation rather than
+  // per-WebContents, in case multiple navigations occur in the WebContents and
+  // the metadata is not cleared. This may be tricky however as the Interceptor
+  // and the Throttle have slightly different views of the navigation -- the
+  // Throttle has a NavigationHandle (and thus the Navigation ID) but the
+  // Interceptor has the NavigationEntry's ID which does not match.
+  bool is_navigation_upgraded_ = false;
+
+  // Set to true if the current navigation is a fallback to HTTP.
+  bool is_navigation_fallback_ = false;
+
+  // Set to true if an interstitial triggered due to an HTTPS timeout is about
+  // to be shown.
+  bool is_timer_interstitial_ = false;
+
+  // HTTP URL that the current navigation should fall back to on failure.
+  GURL fallback_url_;
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+#endif  // CHROME_BROWSER_SSL_HTTPS_ONLY_MODE_TAB_HELPER_H_
diff --git a/chrome/browser/ssl/https_only_mode_tab_storage.cc b/chrome/browser/ssl/https_only_mode_tab_storage.cc
deleted file mode 100644
index 158111d..0000000
--- a/chrome/browser/ssl/https_only_mode_tab_storage.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ssl/https_only_mode_tab_storage.h"
-
-#include <memory>
-
-#include "content/public/browser/web_contents.h"
-
-// This bit of chaos ensures that kHttpsOnlyModeStorageKey is an arbitrary but
-// unique-in-the-process value (namely, its own memory address) without casts.
-const void* const kHttpsOnlyModeStorageKey = &kHttpsOnlyModeStorageKey;
-
-HttpsOnlyModeTabStorage::HttpsOnlyModeTabStorage() = default;
-HttpsOnlyModeTabStorage::~HttpsOnlyModeTabStorage() = default;
-
-// static
-HttpsOnlyModeTabStorage* HttpsOnlyModeTabStorage::GetOrCreate(
-    content::WebContents* web_contents) {
-  auto* existing_storage = static_cast<HttpsOnlyModeTabStorage*>(
-      web_contents->GetUserData(kHttpsOnlyModeStorageKey));
-  if (existing_storage) {
-    return existing_storage;
-  }
-
-  auto new_storage = std::make_unique<HttpsOnlyModeTabStorage>();
-  auto* new_storage_ptr = new_storage.get();
-  web_contents->SetUserData(kHttpsOnlyModeStorageKey, std::move(new_storage));
-  return new_storage_ptr;
-}
-
-void HttpsOnlyModeTabStorage::AddHostToAllowlist(const std::string& hostname) {
-  allowlist_.insert(hostname);
-}
-
-bool HttpsOnlyModeTabStorage::IsHostAllowlisted(const std::string& hostname) {
-  auto it = allowlist_.find(hostname);
-  return it != allowlist_.end();
-}
diff --git a/chrome/browser/ssl/https_only_mode_tab_storage.h b/chrome/browser/ssl/https_only_mode_tab_storage.h
deleted file mode 100644
index c369537..0000000
--- a/chrome/browser/ssl/https_only_mode_tab_storage.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SSL_HTTPS_ONLY_MODE_TAB_STORAGE_H_
-#define CHROME_BROWSER_SSL_HTTPS_ONLY_MODE_TAB_STORAGE_H_
-
-#include <set>
-#include <string>
-
-#include "base/supports_user_data.h"
-
-namespace content {
-class WebContents;
-}  // namespace content
-
-// A short-lived, per-tab storage for the HTTPS-Only Mode allowlist and data
-// about the navigation.
-class HttpsOnlyModeTabStorage : public base::SupportsUserData::Data {
- public:
-  HttpsOnlyModeTabStorage();
-  ~HttpsOnlyModeTabStorage() override;
-
-  HttpsOnlyModeTabStorage(const HttpsOnlyModeTabStorage&) = delete;
-  HttpsOnlyModeTabStorage& operator=(const HttpsOnlyModeTabStorage&) = delete;
-
-  static HttpsOnlyModeTabStorage* GetOrCreate(
-      content::WebContents* web_contents);
-
-  void AddHostToAllowlist(const std::string& hostname);
-  bool IsHostAllowlisted(const std::string& hostname);
-
-  void set_is_navigation_upgraded(bool upgraded) {
-    is_navigation_upgraded_ = upgraded;
-  }
-  bool is_navigation_upgraded() const { return is_navigation_upgraded_; }
-
- private:
-  // TODO(crbug.com/1218526): Track upgrade status per-navigation rather than
-  // per-WebContents, in case multiple navigations occur in the WebContents and
-  // the metadata is not cleared. This may be tricky however as the Interceptor
-  // and the Throttle have slightly different views of the navigation -- the
-  // Throttle has a NavigationHandle (and thus the Navigation ID) but the
-  // Interceptor has the NavigationEntry's ID which does not match.
-  bool is_navigation_upgraded_ = false;
-
-  // A simple tab-scoped allowlist of hostnames.
-  // TODO(crbug.com/1218526): Replace with persistent allowlist implementation.
-  std::set<std::string> allowlist_;
-};
-
-#endif  // CHROME_BROWSER_SSL_HTTPS_ONLY_MODE_TAB_STORAGE_H_
diff --git a/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc b/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc
index f1f7ce8..8740fe6f 100644
--- a/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc
+++ b/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc
@@ -5,10 +5,12 @@
 #include "chrome/browser/ssl/https_only_mode_upgrade_interceptor.h"
 
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ssl/https_only_mode_tab_storage.h"
+#include "chrome/browser/ssl/https_only_mode_tab_helper.h"
 #include "chrome/browser/ssl/https_only_mode_upgrade_url_loader.h"
+#include "chrome/browser/ssl/stateful_ssl_host_state_delegate_factory.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h"
 #include "content/public/browser/web_contents.h"
 #include "net/base/url_util.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
@@ -27,10 +29,12 @@
 // TODO(crbug.com/1218526): Consider excluding IP addresses and non-unique
 // hostnames (as these are likely intranet or unable to have publicly trusted
 // certificates).
-bool ShouldCreateLoader(const network::ResourceRequest& resource_request) {
+bool ShouldCreateLoader(const network::ResourceRequest& resource_request,
+                        HttpsOnlyModeTabHelper* tab_helper) {
   if (resource_request.resource_type !=
           static_cast<int>(blink::mojom::ResourceType::kMainFrame) ||
-      !resource_request.url.SchemeIs(url::kHttpScheme) ||
+      (!resource_request.url.SchemeIs(url::kHttpScheme) &&
+       !tab_helper->is_navigation_fallback()) ||
       resource_request.method != "GET" || !resource_request.is_main_frame) {
     return false;
   }
@@ -66,22 +70,40 @@
     return;
   }
 
-  // Do not upgrade if the hostname is allowlisted.
-  auto* tab_storage = HttpsOnlyModeTabStorage::GetOrCreate(
-      content::WebContents::FromFrameTreeNodeId(frame_tree_node_id_));
-  if (tab_storage->IsHostAllowlisted(tentative_resource_request.url.host())) {
-    std::move(callback).Run({});
-    return;
-  }
+  auto* web_contents =
+      content::WebContents::FromFrameTreeNodeId(frame_tree_node_id_);
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
 
-  if (ShouldCreateLoader(tentative_resource_request)) {
-    // Mark the navigation as upgraded.
-    auto* web_contents =
-        content::WebContents::FromFrameTreeNodeId(frame_tree_node_id_);
-    auto* tab_storage = HttpsOnlyModeTabStorage::GetOrCreate(web_contents);
-    tab_storage->set_is_navigation_upgraded(true);
+  auto* tab_helper = HttpsOnlyModeTabHelper::FromWebContents(web_contents);
+  if (ShouldCreateLoader(tentative_resource_request, tab_helper)) {
+    // If the navigation is a fallback, redirect to the original URL.
+    if (tab_helper->is_navigation_fallback()) {
+      tab_helper->set_is_navigation_fallback(false);
+      CreateHttpsRedirectLoader(tentative_resource_request,
+                                std::move(callback));
+      redirect_url_loader_->StartRedirectToOriginalURL(
+          tab_helper->fallback_url());
+      return;
+    }
 
+    // Don't upgrade navigation if it is allowlisted.
+    StatefulSSLHostStateDelegate* state =
+        static_cast<StatefulSSLHostStateDelegate*>(
+            profile->GetSSLHostStateDelegate());
+    // StatefulSSLHostStateDelegate can be null during tests.
+    if (state &&
+        state->IsHttpAllowedForHost(tentative_resource_request.url.host())) {
+      std::move(callback).Run({});
+      return;
+    }
+
+    // Mark navigation as upgraded.
+    tab_helper->set_is_navigation_upgraded(true);
+    tab_helper->set_fallback_url(tentative_resource_request.url);
     CreateHttpsRedirectLoader(tentative_resource_request, std::move(callback));
+    // `redirect_url_loader_` can be null after this call.
+    redirect_url_loader_->StartRedirectToHttps(frame_tree_node_id_);
     return;
   }
 
@@ -120,9 +142,6 @@
       tentative_resource_request,
       base::BindOnce(&HttpsOnlyModeUpgradeInterceptor::HandleRedirectLoader,
                      base::Unretained(this), std::move(callback)));
-
-  // `redirect_url_loader_` can be null after this call.
-  redirect_url_loader_->StartRedirectToHttps(frame_tree_node_id_);
 }
 
 // Runs `callback` with `handler`.
diff --git a/chrome/browser/ssl/https_only_mode_upgrade_url_loader.cc b/chrome/browser/ssl/https_only_mode_upgrade_url_loader.cc
index 6c049fc..fc8d6c3 100644
--- a/chrome/browser/ssl/https_only_mode_upgrade_url_loader.cc
+++ b/chrome/browser/ssl/https_only_mode_upgrade_url_loader.cc
@@ -71,6 +71,15 @@
       weak_ptr_factory_.GetWeakPtr()));
 }
 
+void HttpsOnlyModeUpgradeURLLoader::StartRedirectToOriginalURL(
+    const GURL& original_url) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CreateRedirectInformation(original_url);
+  std::move(callback_).Run(base::BindOnce(
+      &HttpsOnlyModeUpgradeURLLoader::StartHandlingRedirectToModifiedRequest,
+      weak_ptr_factory_.GetWeakPtr()));
+}
+
 void HttpsOnlyModeUpgradeURLLoader::FollowRedirect(
     const std::vector<std::string>& removed_headers,
     const net::HttpRequestHeaders& modified_headers,
diff --git a/chrome/browser/ssl/https_only_mode_upgrade_url_loader.h b/chrome/browser/ssl/https_only_mode_upgrade_url_loader.h
index ae9fa878..3b6c5e7 100644
--- a/chrome/browser/ssl/https_only_mode_upgrade_url_loader.h
+++ b/chrome/browser/ssl/https_only_mode_upgrade_url_loader.h
@@ -34,6 +34,8 @@
 
   void StartRedirectToHttps(int frame_tree_node_id);
 
+  void StartRedirectToOriginalURL(const GURL& original_url);
+
  private:
   // network::mojom::URLLoader:
   void FollowRedirect(
diff --git a/chrome/browser/ssl/stateful_ssl_host_state_delegate_test.cc b/chrome/browser/ssl/stateful_ssl_host_state_delegate_test.cc
index 32f738c..146868b 100644
--- a/chrome/browser/ssl/stateful_ssl_host_state_delegate_test.cc
+++ b/chrome/browser/ssl/stateful_ssl_host_state_delegate_test.cc
@@ -737,32 +737,3 @@
                                net::ERR_CERT_DATE_INVALID, tab));
   EXPECT_FALSE(state->HasAllowException(kWWWGoogleHost, tab));
 }
-
-// When the flag is set, requests to localhost with invalid certificates
-// should be allowed.
-class AllowLocalhostErrorsSSLHostStateDelegateTest
-    : public StatefulSSLHostStateDelegateTest {
- protected:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    StatefulSSLHostStateDelegateTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kAllowInsecureLocalhost);
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(AllowLocalhostErrorsSSLHostStateDelegateTest,
-                       LocalhostErrorWithFlag) {
-  // Serve the Google cert for localhost to generate an error.
-  scoped_refptr<net::X509Certificate> cert = GetOkCert();
-  content::WebContents* tab =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
-  content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
-
-  EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
-            state->QueryPolicy("localhost", *cert,
-                               net::ERR_CERT_COMMON_NAME_INVALID, tab));
-
-  EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
-            state->QueryPolicy("127.0.0.1", *cert,
-                               net::ERR_CERT_COMMON_NAME_INVALID, tab));
-}
diff --git a/chrome/browser/tab/BUILD.gn b/chrome/browser/tab/BUILD.gn
index a6c16cd..3a662c5 100644
--- a/chrome/browser/tab/BUILD.gn
+++ b/chrome/browser/tab/BUILD.gn
@@ -19,6 +19,7 @@
     "java/src/org/chromium/chrome/browser/tab/TabCreationState.java",
     "java/src/org/chromium/chrome/browser/tab/TabDelegateFactory.java",
     "java/src/org/chromium/chrome/browser/tab/TabHidingType.java",
+    "java/src/org/chromium/chrome/browser/tab/TabIdManager.java",
     "java/src/org/chromium/chrome/browser/tab/TabLifecycle.java",
     "java/src/org/chromium/chrome/browser/tab/TabObserver.java",
     "java/src/org/chromium/chrome/browser/tab/TabResolver.java",
@@ -69,6 +70,7 @@
     "//chrome/browser/flags:java",
     "//chrome/browser/optimization_guide/android:java",
     "//chrome/browser/page_annotations/android:java",
+    "//chrome/browser/preferences:java",
     "//chrome/browser/profiles/android:java",
     "//chrome/browser/ui/android/native_page:java",
     "//chrome/browser/ui/android/strings:ui_strings_grd",
@@ -139,6 +141,7 @@
   sources = [
     "java/src/org/chromium/chrome/browser/tab/CurrentTabObserverTest.java",
     "java/src/org/chromium/chrome/browser/tab/TabAssociatedAppTest.java",
+    "java/src/org/chromium/chrome/browser/tab/TabIdManagerTest.java",
   ]
 
   deps = [
@@ -146,6 +149,7 @@
     "//base:base_java",
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
+    "//chrome/browser/preferences:java",
     "//content/public/android:content_full_java",
     "//third_party/android_deps:robolectric_all_java",
     "//third_party/android_support_test_runner:runner_java",
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/DEPS b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/DEPS
index c45a072ea..0c546b8 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/DEPS
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/DEPS
@@ -9,6 +9,7 @@
   "+chrome/browser/endpoint_fetcher",
   "+chrome/browser/tab/java",
   "+chrome/browser/optimization_guide/android/java/src/org/chromium/chrome/browser/optimization_guide",
+  "+chrome/browser/preferences/android/java",
   "+chrome/browser/profiles/android/java",
   "+chrome/browser/ui/android/native_page",
   "+chrome/browser/flags/android/java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabIdManager.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabIdManager.java
similarity index 94%
rename from chrome/android/java/src/org/chromium/chrome/browser/tab/TabIdManager.java
rename to chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabIdManager.java
index f242cfa..531df3eb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabIdManager.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabIdManager.java
@@ -12,15 +12,14 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.browser.tabmodel.TabModel;
 
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Maintains a monotonically increasing ID that is used for uniquely identifying {@link Tab}s.  This
- * class is responsible for ensuring that Tabs created in the same process, across every
- * {@link TabModel}, are allocated a unique ID.  Note that only the browser process should be
- * generating Tab IDs to prevent collisions.
+ * class is responsible for ensuring that Tabs created in the same process, across every TabModel,
+ * are allocated a unique ID.  Note that only the browser process should be generating Tab IDs to
+ * prevent collisions.
  *
  * Calling {@link TabIdManager#incrementIdCounterTo(int)} will ensure new {@link Tab}s get IDs
  * greater than or equal to the parameter passed to that method.  This should be used when doing
@@ -31,7 +30,6 @@
  *                    started first.  Unify the ways the maximum Tab ID is set (crbug.com/502384).
  */
 public class TabIdManager {
-
     private static final Object INSTANCE_LOCK = new Object();
     @SuppressLint("StaticFieldLeak")
     private static TabIdManager sInstance;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabIdManagerTest.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabIdManagerTest.java
similarity index 81%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabIdManagerTest.java
rename to chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabIdManagerTest.java
index 9a15134..4b1684f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabIdManagerTest.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabIdManagerTest.java
@@ -5,32 +5,30 @@
 package org.chromium.chrome.browser.tab;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-
-import androidx.test.filters.SmallTest;
 
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
 
-import org.chromium.base.test.UiThreadTest;
-import org.chromium.base.test.util.AdvancedMockContext;
-import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 
 /** Tests for the TabIdManager. */
-@RunWith(ChromeJUnit4ClassRunner.class)
-@Batch(Batch.UNIT_TESTS)
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
 public class TabIdManagerTest {
-    Context mContext;
+    @Mock
+    private Context mContext;
 
     @Before
     public void setUp() {
-        mContext = new AdvancedMockContext(InstrumentationRegistry.getTargetContext());
+        MockitoAnnotations.initMocks(this);
         TabIdManager.resetInstanceForTesting();
     }
 
@@ -41,8 +39,6 @@
 
     /** Tests that IDs are stored and generated properly. */
     @Test
-    @UiThreadTest
-    @SmallTest
     public void testBasic() {
         SharedPreferencesManager prefs = SharedPreferencesManager.getInstance();
         prefs.writeInt(ChromePreferenceKeys.TAB_ID_MANAGER_NEXT_ID, 11684);
@@ -57,8 +53,6 @@
 
     /** Tests that the max ID is updated properly. */
     @Test
-    @UiThreadTest
-    @SmallTest
     public void testIncrementIdCounterTo() {
         SharedPreferencesManager prefs = SharedPreferencesManager.getInstance();
         prefs.writeInt(ChromePreferenceKeys.TAB_ID_MANAGER_NEXT_ID, 11684);
diff --git a/chrome/browser/touch_to_fill/BUILD.gn b/chrome/browser/touch_to_fill/BUILD.gn
index ef1f4a3d..fb4eb70 100644
--- a/chrome/browser/touch_to_fill/BUILD.gn
+++ b/chrome/browser/touch_to_fill/BUILD.gn
@@ -29,7 +29,6 @@
 source_set("public") {
   deps = [
     "//base",
-    "//base/util/type_safety",
     "//url",
   ]
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index ba29510..16d34b64 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1592,13 +1592,13 @@
       "//chrome/browser/browsing_data:constants",
       "//chrome/browser/cart:mojo_bindings",
       "//chrome/browser/media/router",
+      "//chrome/browser/new_tab_page/modules/drive:mojo_bindings",
+      "//chrome/browser/new_tab_page/modules/task_module:mojo_bindings",
       "//chrome/browser/profile_resetter:profile_reset_report_proto",
       "//chrome/browser/promo_browser_command:mojo_bindings",
       "//chrome/browser/resource_coordinator:tab_metrics_event_proto",
       "//chrome/browser/resource_coordinator/tab_ranker",
       "//chrome/browser/safe_browsing:advanced_protection",
-      "//chrome/browser/search/drive:mojo_bindings",
-      "//chrome/browser/search/task_module:mojo_bindings",
       "//chrome/browser/ui/color:color_headers",
       "//chrome/browser/ui/color:mixers",
       "//chrome/browser/ui/commander:fuzzy_finder",
@@ -2142,6 +2142,8 @@
       "ash/shelf/shelf_spinner_item_controller.h",
       "ash/system_tray_client_impl.cc",
       "ash/system_tray_client_impl.h",
+      "ash/tab_cluster_ui_client.cc",
+      "ash/tab_cluster_ui_client.h",
       "ash/tab_scrubber.cc",
       "ash/tab_scrubber.h",
       "ash/tablet_mode_page_behavior.cc",
@@ -4448,6 +4450,8 @@
       "views/web_apps/web_app_confirmation_view.h",
       "views/web_apps/web_app_hover_button.cc",
       "views/web_apps/web_app_hover_button.h",
+      "views/web_apps/web_app_identity_update_confirmation_view.cc",
+      "views/web_apps/web_app_identity_update_confirmation_view.h",
       "views/web_apps/web_app_info_image_source.cc",
       "views/web_apps/web_app_info_image_source.h",
       "views/web_apps/web_app_protocol_handler_intent_picker_dialog_view.cc",
diff --git a/chrome/browser/ui/android/omnibox/BUILD.gn b/chrome/browser/ui/android/omnibox/BUILD.gn
index f1f14bb..93efa99b 100644
--- a/chrome/browser/ui/android/omnibox/BUILD.gn
+++ b/chrome/browser/ui/android/omnibox/BUILD.gn
@@ -108,6 +108,7 @@
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderView.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewBinder.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewProperties.java",
+    "java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/ExploreIconProvider.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/AlignmentManager.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/TailSuggestionProcessor.java",
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
index 3a8dc74..abcd05d 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
@@ -35,6 +35,7 @@
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteDelegate;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionsDropdownEmbedder;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.BasicSuggestionProcessor.BookmarkState;
+import org.chromium.chrome.browser.omnibox.suggestions.mostvisited.ExploreIconProvider;
 import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
 import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -128,6 +129,7 @@
      * @param tabWindowManagerSupplier Supplier of glue-level TabWindowManager object.
      * @param bookmarkState State of a URL bookmark state.
      * @param isToolbarMicEnabledSupplier Whether toolbar mic is enabled or not.
+     * @param exploreIconProvider The provider to get explore sites icon.
      */
     public LocationBarCoordinator(View locationBarLayout, View autocompleteAnchorView,
             ObservableSupplier<Profile> profileObservableSupplier,
@@ -146,7 +148,8 @@
             @NonNull SaveOfflineButtonState saveOfflineButtonState, @NonNull OmniboxUma omniboxUma,
             @NonNull Supplier<TabWindowManager> tabWindowManagerSupplier,
             @NonNull BookmarkState bookmarkState,
-            @NonNull BooleanSupplier isToolbarMicEnabledSupplier, JankTracker jankTracker) {
+            @NonNull BooleanSupplier isToolbarMicEnabledSupplier, JankTracker jankTracker,
+            @NonNull ExploreIconProvider exploreIconProvider) {
         mLocationBarLayout = (LocationBarLayout) locationBarLayout;
         mWindowDelegate = windowDelegate;
         mWindowAndroid = windowAndroid;
@@ -168,10 +171,11 @@
                 new UrlBarCoordinator((UrlBar) mUrlBar, windowDelegate, actionModeCallback,
                         mCallbackController.makeCancelable(mLocationBarMediator::onUrlFocusChange),
                         mLocationBarMediator, windowAndroid.getKeyboardDelegate());
-        mAutocompleteCoordinator = new AutocompleteCoordinator(mLocationBarLayout, this, this,
-                mUrlCoordinator, modalDialogManagerSupplier, activityTabSupplier,
-                shareDelegateSupplier, locationBarDataProvider, profileObservableSupplier,
-                bringTabToFrontCallback, tabWindowManagerSupplier, bookmarkState, jankTracker);
+        mAutocompleteCoordinator =
+                new AutocompleteCoordinator(mLocationBarLayout, this, this, mUrlCoordinator,
+                        modalDialogManagerSupplier, activityTabSupplier, shareDelegateSupplier,
+                        locationBarDataProvider, profileObservableSupplier, bringTabToFrontCallback,
+                        tabWindowManagerSupplier, bookmarkState, jankTracker, exploreIconProvider);
         StatusView statusView = mLocationBarLayout.findViewById(R.id.location_bar_status);
         mStatusCoordinator = new StatusCoordinator(isTablet(), statusView, mUrlCoordinator,
                 incognitoStateProvider, modalDialogManagerSupplier, locationBarDataProvider,
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
index e7cd9b5..0bf5053 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
@@ -39,6 +39,7 @@
 import org.chromium.chrome.browser.omnibox.suggestions.entity.EntitySuggestionViewBinder;
 import org.chromium.chrome.browser.omnibox.suggestions.header.HeaderView;
 import org.chromium.chrome.browser.omnibox.suggestions.header.HeaderViewBinder;
+import org.chromium.chrome.browser.omnibox.suggestions.mostvisited.ExploreIconProvider;
 import org.chromium.chrome.browser.omnibox.suggestions.mostvisited.MostVisitedTilesProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.tail.TailSuggestionView;
 import org.chromium.chrome.browser.omnibox.suggestions.tail.TailSuggestionViewBinder;
@@ -82,7 +83,8 @@
             @NonNull ObservableSupplier<Profile> profileObservableSupplier,
             @NonNull Callback<Tab> bringToForegroundCallback,
             @NonNull Supplier<TabWindowManager> tabWindowManagerSupplier,
-            @NonNull BookmarkState bookmarkState, @NonNull JankTracker jankTracker) {
+            @NonNull BookmarkState bookmarkState, @NonNull JankTracker jankTracker,
+            @NonNull ExploreIconProvider exploreIconProvider) {
         mParent = parent;
         Context context = parent.getContext();
 
@@ -97,7 +99,7 @@
         mMediator = new AutocompleteMediator(context, delegate, urlBarEditingTextProvider,
                 listModel, new Handler(), modalDialogManagerSupplier, activityTabSupplier,
                 shareDelegateSupplier, locationBarDataProvider, bringToForegroundCallback,
-                tabWindowManagerSupplier, bookmarkState, jankTracker);
+                tabWindowManagerSupplier, bookmarkState, jankTracker, exploreIconProvider);
         mMediator.initDefaultProcessors(mQueryTileCoordinator::setTiles);
 
         listModel.set(SuggestionListProperties.OBSERVER, mMediator);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index 537a20e..528438d 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -33,6 +33,7 @@
 import org.chromium.chrome.browser.omnibox.styles.OmniboxTheme;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteController.OnSuggestionsReceivedListener;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.BasicSuggestionProcessor.BookmarkState;
+import org.chromium.chrome.browser.omnibox.suggestions.mostvisited.ExploreIconProvider;
 import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.share.ShareDelegate;
@@ -146,7 +147,8 @@
             @NonNull LocationBarDataProvider locationBarDataProvider,
             @NonNull Callback<Tab> bringTabToFrontCallback,
             @NonNull Supplier<TabWindowManager> tabWindowManagerSupplier,
-            @NonNull BookmarkState bookmarkState, @NonNull JankTracker jankTracker) {
+            @NonNull BookmarkState bookmarkState, @NonNull JankTracker jankTracker,
+            @NonNull ExploreIconProvider exploreIconProvider) {
         mContext = context;
         mDelegate = delegate;
         mUrlBarEditingTextProvider = textProvider;
@@ -158,8 +160,8 @@
         mBringTabToFrontCallback = bringTabToFrontCallback;
         mTabWindowManagerSupplier = tabWindowManagerSupplier;
         mSuggestionModels = mListPropertyModel.get(SuggestionListProperties.SUGGESTION_MODELS);
-        mDropdownViewInfoListBuilder =
-                new DropdownItemViewInfoListBuilder(activityTabSupplier, bookmarkState);
+        mDropdownViewInfoListBuilder = new DropdownItemViewInfoListBuilder(
+                activityTabSupplier, bookmarkState, exploreIconProvider);
         mDropdownViewInfoListBuilder.setShareDelegateSupplier(shareDelegateSupplier);
         mDropdownViewInfoListManager = new DropdownItemViewInfoListManager(mSuggestionModels);
     }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
index 208580a0d..1d03f62 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
@@ -26,6 +26,7 @@
 import org.chromium.chrome.browser.omnibox.suggestions.editurl.EditUrlSuggestionProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.entity.EntitySuggestionProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.header.HeaderProcessor;
+import org.chromium.chrome.browser.omnibox.suggestions.mostvisited.ExploreIconProvider;
 import org.chromium.chrome.browser.omnibox.suggestions.mostvisited.MostVisitedTilesProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.tail.TailSuggestionProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.tiles.TileSuggestionProcessor;
@@ -59,17 +60,19 @@
     private @Nullable ImageFetcher mImageFetcher;
     private @Nullable LargeIconBridge mIconBridge;
     private @NonNull BookmarkState mBookmarkState;
+    private @NonNull ExploreIconProvider mExploreIconProvider;
     @Px
     private int mDropdownHeight;
     private boolean mEnableAdaptiveSuggestionsCount;
     private boolean mBuiltListHasFullyConcealedElements;
 
-    DropdownItemViewInfoListBuilder(
-            @NonNull Supplier<Tab> tabSupplier, BookmarkState bookmarkState) {
+    DropdownItemViewInfoListBuilder(@NonNull Supplier<Tab> tabSupplier, BookmarkState bookmarkState,
+            @NonNull ExploreIconProvider exploreIconProvider) {
         mPriorityOrderedSuggestionProcessors = new ArrayList<>();
         mDropdownHeight = DROPDOWN_HEIGHT_UNKNOWN;
         mActivityTabSupplier = tabSupplier;
         mBookmarkState = bookmarkState;
+        mExploreIconProvider = exploreIconProvider;
     }
 
     /**
@@ -103,8 +106,8 @@
         registerSuggestionProcessor(new TailSuggestionProcessor(context, host));
         registerSuggestionProcessor(
                 new TileSuggestionProcessor(context, queryTileSuggestionCallback));
-        registerSuggestionProcessor(
-                new MostVisitedTilesProcessor(context, host, iconBridgeSupplier));
+        registerSuggestionProcessor(new MostVisitedTilesProcessor(context, host, iconBridgeSupplier,
+                mExploreIconProvider, GlobalDiscardableReferencePool.getReferencePool()));
         registerSuggestionProcessor(new BasicSuggestionProcessor(
                 context, host, textProvider, iconBridgeSupplier, mBookmarkState));
     }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/ExploreIconProvider.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/ExploreIconProvider.java
new file mode 100644
index 0000000..c869096
--- /dev/null
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/ExploreIconProvider.java
@@ -0,0 +1,18 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.omnibox.suggestions.mostvisited;
+
+import android.graphics.Bitmap;
+
+import org.chromium.base.Callback;
+
+/** The provider of the explore sites icon. */
+public interface ExploreIconProvider {
+    /**
+     * @param pixelSize The icon width in pixel.
+     * @param callback Called when the icon is available.
+     */
+    void getSummaryImage(int pixelSize, Callback<Bitmap> callback);
+}
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java
index 46cf1dfd..c7eda69 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java
@@ -8,12 +8,17 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
 
 import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.DiscardableReferencePool;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.R;
@@ -28,8 +33,10 @@
 import org.chromium.components.browser_ui.widget.tile.TileView;
 import org.chromium.components.browser_ui.widget.tile.TileViewBinder;
 import org.chromium.components.browser_ui.widget.tile.TileViewProperties;
+import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.favicon.LargeIconBridge;
 import org.chromium.components.omnibox.AutocompleteMatch;
+import org.chromium.ui.base.ViewUtils;
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -52,19 +59,29 @@
     private final RoundedIconGenerator mIconGenerator;
     private static final int DEFAULT_TILE_TYPE = 0;
 
+    private @NonNull ExploreIconProvider mExploreIconProvider;
+    private @NonNull DiscardableReferencePool mReferencePool;
+    private @Nullable DiscardableReferencePool
+            .DiscardableReference<RoundedBitmapDrawable> mExploreSitesIcon;
+
     /**
      * Constructor.
      *
      * @param context An Android context.
      * @param host SuggestionHost receiving notifications about user actions.
      * @param iconBridgeSupplier Supplier of the LargeIconBridge used to fetch site favicons.
+     * @param exploreIconProvider The provider to get the large icon of explore sites.
      */
     public MostVisitedTilesProcessor(@NonNull Context context, @NonNull SuggestionHost host,
-            @NonNull Supplier<LargeIconBridge> iconBridgeSupplier) {
+            @NonNull Supplier<LargeIconBridge> iconBridgeSupplier,
+            @NonNull ExploreIconProvider exploreIconProvider,
+            @NonNull DiscardableReferencePool referencePool) {
         super(context);
         mContext = context;
         mSuggestionHost = host;
         mIconBridgeSupplier = iconBridgeSupplier;
+        mExploreIconProvider = exploreIconProvider;
+        mReferencePool = referencePool;
         mMinCarouselItemViewHeight =
                 mContext.getResources().getDimensionPixelSize(R.dimen.tile_view_min_height);
         mDesiredFaviconWidthPx = mContext.getResources().getDimensionPixelSize(
@@ -78,6 +95,7 @@
                 mContext.getResources().getDimensionPixelSize(R.dimen.tile_view_icon_text_size);
         mIconGenerator = new RoundedIconGenerator(fallbackIconSize, fallbackIconSize,
                 fallbackIconSize / 2, fallbackIconColor, fallbackIconTextSize);
+        mExploreSitesIcon = null;
     }
 
     @Override
@@ -129,7 +147,9 @@
             Bitmap fallbackIcon = mIconGenerator.generateIconForUrl(url);
             tileModel.set(TileViewProperties.ICON, new BitmapDrawable(fallbackIcon));
 
-            if (iconBridge != null) {
+            if (TextUtils.equals(url.getSpec(), UrlConstants.EXPLORE_URL)) {
+                setExploreSitesIcon(tileModel);
+            } else if (iconBridge != null) {
                 iconBridge.getLargeIconForUrl(tiles.get(index).url, mDesiredFaviconWidthPx,
                         (Bitmap icon, int fallbackColor, boolean isFallbackColorDefault,
                                 int iconType) -> {
@@ -146,6 +166,44 @@
     }
 
     /**
+     * Sets the Explore sites icon to the property model.
+     */
+    void setExploreSitesIcon(PropertyModel tileModel) {
+        if (mExploreSitesIcon == null || mExploreSitesIcon.get() == null) {
+            // Only fetches the Explore sites icon once per Chrome launch, since the icon
+            // is updated once a month or less.
+            mExploreIconProvider.getSummaryImage(mDesiredFaviconWidthPx, (Bitmap icon) -> {
+                if (icon == null) return;
+
+                RoundedBitmapDrawable roundIcon = getRoundIconFromBitmap(icon);
+                mExploreSitesIcon = mReferencePool.put(roundIcon);
+                setLargeIcon(tileModel, roundIcon);
+            });
+        } else {
+            setLargeIcon(tileModel, mExploreSitesIcon.get());
+        }
+    }
+
+    /**
+     * Gets a round icon from the given bitmap.
+     */
+    private RoundedBitmapDrawable getRoundIconFromBitmap(Bitmap icon) {
+        RoundedBitmapDrawable roundedIcon = ViewUtils.createRoundedBitmapDrawable(
+                mContext.getResources(), icon, mDesiredFaviconWidthPx);
+        roundedIcon.setAntiAlias(true);
+        roundedIcon.setFilterBitmap(true);
+        return roundedIcon;
+    }
+
+    /**
+     * Sets the large icon to the given property model.
+     */
+    private void setLargeIcon(PropertyModel tileModel, RoundedBitmapDrawable icon) {
+        tileModel.set(TileViewProperties.SHOW_LARGE_ICON, true);
+        tileModel.set(TileViewProperties.ICON, icon);
+    }
+
+    /**
      * Create Carousel Suggestion View presenting the Most Visited URL tiles.
      *
      * @param parent ViewGroup that will host the Carousel view.
@@ -174,4 +232,9 @@
         tile.setBackgroundDrawable(background);
         return tile;
     }
+
+    @VisibleForTesting
+    RoundedBitmapDrawable getExploreSitesIconForTesting() {
+        return mExploreSitesIcon != null ? mExploreSitesIcon.get() : null;
+    }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
index 004343f..8b2022c 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
@@ -246,7 +246,7 @@
 
         String url = getCurrentUrl();
         if (NativePage.isNativePageUrl(url, isIncognito()) || UrlUtilities.isNTPUrl(url)
-                || TextUtils.isEmpty(url)) {
+                || StartSurfaceConfiguration.shouldHandleAsNtp(getTab())) {
             return UrlBarData.EMPTY;
         }
 
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonController.java
index 66dbb8a..41d5568c 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonController.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.toolbar.adaptive;
 
+import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.ADAPTIVE_TOOLBAR_CUSTOMIZATION_ENABLED;
 import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.ADAPTIVE_TOOLBAR_CUSTOMIZATION_SETTINGS;
 
 import android.content.Context;
@@ -251,7 +252,8 @@
 
     @Override
     public void onPreferenceChanged(String key) {
-        if (ADAPTIVE_TOOLBAR_CUSTOMIZATION_SETTINGS.equals(key)) {
+        if (ADAPTIVE_TOOLBAR_CUSTOMIZATION_SETTINGS.equals(key)
+                || ADAPTIVE_TOOLBAR_CUSTOMIZATION_ENABLED.equals(key)) {
             assert AdaptiveToolbarFeatures.isCustomizationEnabled();
             new AdaptiveToolbarStatePredictor().recomputeUiState(uiState -> {
                 setSingleProvider(uiState.canShowUi
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/OptionalBrowsingModeButtonController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/OptionalBrowsingModeButtonController.java
index dbf5e74..740342c0 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/OptionalBrowsingModeButtonController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/OptionalBrowsingModeButtonController.java
@@ -129,7 +129,7 @@
      */
     private void updateCurrentOptionalButton(ButtonDataProvider provider) {
         ButtonData buttonData = provider.get(mTabSupplier.get());
-        if (buttonData.canShow()) {
+        if (buttonData != null && buttonData.canShow()) {
             setCurrentOptionalButton(provider, buttonData);
         } else {
             hideCurrentOptionalButton();
diff --git a/chrome/browser/ui/app_list/search/app_service_app_result.cc b/chrome/browser/ui/app_list/search/app_service_app_result.cc
index 3d31429f3f..a4d926d 100644
--- a/chrome/browser/ui/app_list/search/app_service_app_result.cc
+++ b/chrome/browser/ui/app_list/search/app_service_app_result.cc
@@ -129,6 +129,7 @@
     case apps::mojom::AppType::kExtension:
     case apps::mojom::AppType::kWeb:
     case apps::mojom::AppType::kSystemWeb:
+    case apps::mojom::AppType::kStandaloneBrowserExtension:
       return ash::EXTENSION_APP;
     case apps::mojom::AppType::kStandaloneBrowser:
       return ash::LACROS;
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index aeb50c9e..8d7a3d0 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -50,6 +50,7 @@
 #include "chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.h"
 #include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/ash/system_tray_client_impl.h"
+#include "chrome/browser/ui/ash/tab_cluster_ui_client.h"
 #include "chrome/browser/ui/ash/tab_scrubber.h"
 #include "chrome/browser/ui/ash/tablet_mode_page_behavior.h"
 #include "chrome/browser/ui/ash/vpn_list_forwarder.h"
@@ -184,6 +185,14 @@
   system_tray_client_ = std::make_unique<SystemTrayClientImpl>();
   network_connect_delegate_->SetSystemTrayClient(system_tray_client_.get());
 
+  if (ash::features::IsTabClusterUIEnabled()) {
+    ash::TabClusterUIController* tab_cluster_ui_controller =
+        ash::Shell::Get()->tab_cluster_ui_controller();
+    DCHECK(tab_cluster_ui_controller);
+    tab_cluster_ui_client_ =
+        std::make_unique<TabClusterUIClient>(tab_cluster_ui_controller);
+  }
+
   tablet_mode_page_behavior_ = std::make_unique<TabletModePageBehavior>();
   vpn_list_forwarder_ = std::make_unique<VpnListForwarder>();
 
@@ -269,6 +278,8 @@
   wallpaper_controller_client_.reset();
   vpn_list_forwarder_.reset();
 
+  tab_cluster_ui_client_.reset();
+
   // Initialized in PostProfileInit (which may not get called in some tests).
   network_portal_notification_controller_.reset();
   display_settings_handler_.reset();
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
index 72c8084..e1e35be 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
@@ -41,6 +41,7 @@
 class ScreenOrientationDelegateChromeos;
 class SessionControllerClientImpl;
 class SystemTrayClientImpl;
+class TabClusterUIClient;
 class TabletModePageBehavior;
 class VpnListForwarder;
 class WallpaperControllerClientImpl;
@@ -94,6 +95,7 @@
       screen_orientation_delegate_;
   std::unique_ptr<SessionControllerClientImpl> session_controller_client_;
   std::unique_ptr<SystemTrayClientImpl> system_tray_client_;
+  std::unique_ptr<TabClusterUIClient> tab_cluster_ui_client_;
   std::unique_ptr<TabletModePageBehavior> tablet_mode_page_behavior_;
   std::unique_ptr<VpnListForwarder> vpn_list_forwarder_;
   std::unique_ptr<WallpaperControllerClientImpl> wallpaper_controller_client_;
diff --git a/chrome/browser/ui/ash/desk_template_app_launch_handler.cc b/chrome/browser/ui/ash/desk_template_app_launch_handler.cc
index 95e25ca..8289fd6 100644
--- a/chrome/browser/ui/ash/desk_template_app_launch_handler.cc
+++ b/chrome/browser/ui/ash/desk_template_app_launch_handler.cc
@@ -33,13 +33,6 @@
   NOTIMPLEMENTED();
 }
 
-void DeskTemplateAppLaunchHandler::LaunchArcApp(
-    const std::string& app_id,
-    const ::full_restore::RestoreData::LaunchList& launch_list) {
-  // ARC is not currently supported for desk templates.
-  NOTREACHED();
-}
-
 void DeskTemplateAppLaunchHandler::RecordRestoredAppLaunch(
     apps::AppTypeName app_type_name) {
   // TODO: Add UMA Histogram.
diff --git a/chrome/browser/ui/ash/desk_template_app_launch_handler.h b/chrome/browser/ui/ash/desk_template_app_launch_handler.h
index 301be88d..b6e38bf 100644
--- a/chrome/browser/ui/ash/desk_template_app_launch_handler.h
+++ b/chrome/browser/ui/ash/desk_template_app_launch_handler.h
@@ -41,9 +41,6 @@
  private:
   // chromeos::AppLaunchHandler:
   void LaunchBrowser() override;
-  void LaunchArcApp(
-      const std::string& app_id,
-      const full_restore::RestoreData::LaunchList& launch_list) override;
   void RecordRestoredAppLaunch(apps::AppTypeName app_type_name) override;
   void RecordArcGhostWindowLaunch(bool is_arc_ghost_window) override;
 
diff --git a/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc
index e7f41f2..ac87639 100644
--- a/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc
@@ -553,6 +553,7 @@
     case apps::mojom::AppType::kExtension:
     case apps::mojom::AppType::kWeb:
     case apps::mojom::AppType::kSystemWeb:
+    case apps::mojom::AppType::kStandaloneBrowserExtension:
       return true;
     case apps::mojom::AppType::kStandaloneBrowser:
       // Lacros behaves like the Chrome browser icon and cannot be unpinned.
diff --git a/chrome/browser/ui/ash/tab_cluster_ui_client.cc b/chrome/browser/ui/ash/tab_cluster_ui_client.cc
new file mode 100644
index 0000000..791e844
--- /dev/null
+++ b/chrome/browser/ui/ash/tab_cluster_ui_client.cc
@@ -0,0 +1,91 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/ash/tab_cluster_ui_client.h"
+
+#include "ash/public/cpp/tab_cluster/tab_cluster_ui_controller.h"
+#include "ash/public/cpp/tab_cluster/tab_cluster_ui_item.h"
+#include "base/containers/contains.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "content/public/browser/web_contents.h"
+
+namespace {
+
+// Generate tab item info from a web contents.
+ash::TabClusterUIItem::Info GenerateTabItemInfo(
+    content::WebContents* web_contents) {
+  ash::TabClusterUIItem::Info info;
+  info.title = base::UTF16ToUTF8(web_contents->GetTitle());
+  info.source = web_contents->GetVisibleURL().spec();
+  info.browser_window = chrome::FindBrowserWithWebContents(web_contents)
+                            ->window()
+                            ->GetNativeWindow();
+  return info;
+}
+
+}  //  namespace
+
+TabClusterUIClient::TabClusterUIClient(ash::TabClusterUIController* controller)
+    : controller_(controller), browser_tab_strip_tracker_(this, nullptr) {
+  browser_tab_strip_tracker_.Init();
+}
+
+TabClusterUIClient::~TabClusterUIClient() = default;
+
+void TabClusterUIClient::OnTabStripModelChanged(
+    TabStripModel* tab_strip_model,
+    const TabStripModelChange& change,
+    const TabStripSelectionChange& selection) {
+  switch (change.type()) {
+    case TabStripModelChange::kInserted:
+      // Add new items corresponding to the inserted web contents.
+      for (const auto& contents : change.GetInsert()->contents) {
+        content::WebContents* web_contents = contents.contents;
+        auto* item =
+            controller_->AddTabItem(std::make_unique<ash::TabClusterUIItem>(
+                GenerateTabItemInfo(web_contents)));
+        contents_item_map_[web_contents] = item;
+      }
+      break;
+    case TabStripModelChange::kRemoved:
+      // Remove the items corresponding to the removed web contents.
+      for (const auto& contents : change.GetRemove()->contents) {
+        content::WebContents* web_contents = contents.contents;
+        DCHECK(base::Contains(contents_item_map_, web_contents));
+        ash::TabClusterUIItem* item = contents_item_map_[web_contents];
+        contents_item_map_.erase(web_contents);
+        controller_->RemoveTabItem(item);
+      }
+      break;
+    case TabStripModelChange::kReplaced:
+      // Update the item whose corresponding contents are replaced.
+      {
+        auto* replace = change.GetReplace();
+        DCHECK(base::Contains(contents_item_map_, replace->old_contents));
+        auto* item = contents_item_map_[replace->old_contents];
+
+        item->Init(GenerateTabItemInfo(replace->new_contents));
+        controller_->UpdateTabItem(item);
+
+        contents_item_map_[replace->new_contents] = item;
+        contents_item_map_.erase(replace->old_contents);
+      }
+      break;
+    case TabStripModelChange::kMoved:
+      break;
+    case TabStripModelChange::kSelectionOnly:
+      break;
+  }
+}
+
+void TabClusterUIClient::TabChangedAt(content::WebContents* contents,
+                                      int index,
+                                      TabChangeType change_type) {
+  DCHECK(base::Contains(contents_item_map_, contents));
+  auto* item = contents_item_map_[contents];
+  item->Init(GenerateTabItemInfo(contents));
+  controller_->UpdateTabItem(item);
+}
diff --git a/chrome/browser/ui/ash/tab_cluster_ui_client.h b/chrome/browser/ui/ash/tab_cluster_ui_client.h
new file mode 100644
index 0000000..50c6be0
--- /dev/null
+++ b/chrome/browser/ui/ash/tab_cluster_ui_client.h
@@ -0,0 +1,52 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_ASH_TAB_CLUSTER_UI_CLIENT_H_
+#define CHROME_BROWSER_UI_ASH_TAB_CLUSTER_UI_CLIENT_H_
+
+#include <map>
+
+#include "chrome/browser/ui/browser_tab_strip_tracker.h"
+#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
+
+namespace ash {
+class TabClusterUIItem;
+class TabClusterUIController;
+}  // namespace ash
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+// TabClusterUIClient:
+// Inherits from `TabStripModelObserver` to collect tab info from browser.
+// `TabClusterUIClient` observes tab strip and generates
+// `ash::TabClusterUIItem::Info` according to the tabs opened in the browser.
+// The `ash::TabClusterUIItem` is created based on the info and sent to
+// `ash::TabClusterUIController` for management.
+class TabClusterUIClient : public TabStripModelObserver {
+ public:
+  explicit TabClusterUIClient(ash::TabClusterUIController* controller);
+  TabClusterUIClient(TabClusterUIClient&) = delete;
+  TabClusterUIClient& operator=(TabClusterUIClient&) = delete;
+
+  ~TabClusterUIClient() override;
+
+  // TabStripModelObserver:
+  void OnTabStripModelChanged(
+      TabStripModel* tab_strip_model,
+      const TabStripModelChange& change,
+      const TabStripSelectionChange& selection) override;
+  void TabChangedAt(content::WebContents* contents,
+                    int index,
+                    TabChangeType change_type) override;
+
+ private:
+  ash::TabClusterUIController* controller_;
+  BrowserTabStripTracker browser_tab_strip_tracker_;
+  // A map from web contents to tab items.
+  std::map<content::WebContents*, ash::TabClusterUIItem*> contents_item_map_;
+};
+
+#endif  // CHROME_BROWSER_UI_ASH_TAB_CLUSTER_UI_CLIENT_H_
diff --git a/chrome/browser/ui/browser_dialogs.h b/chrome/browser/ui/browser_dialogs.h
index d5f1a9a..940fe19e 100644
--- a/chrome/browser/ui/browser_dialogs.h
+++ b/chrome/browser/ui/browser_dialogs.h
@@ -14,6 +14,7 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/ui/bookmarks/bookmark_editor.h"
+#include "chrome/browser/web_applications/components/web_app_callback_app_identity.h"
 #include "chrome/browser/web_applications/components/web_app_id.h"
 #include "chrome/common/buildflags.h"
 #include "content/public/browser/content_browser_client.h"
@@ -133,6 +134,29 @@
                              std::unique_ptr<WebApplicationInfo> web_app_info,
                              AppInstallationAcceptanceCallback callback);
 
+// When an app changes its icon or name, that is considered an app identity
+// change which (for some types of apps) needs confirmation from the user.
+// This function shows that confirmation dialog. |app_id| is the unique id of
+// the app that is updating and |title_change| and |icon_change| specify which
+// piece of information is changing. Can be one or the other, or both (but
+// both cannot be |false|). |old_title| and |new_title|, as well as |old_icon|
+// and |new_icon| show the 'before' and 'after' values. A response is sent
+// back via the |callback|.
+void ShowWebAppIdentityUpdateDialog(
+    const std::string& app_id,
+    bool title_change,
+    bool icon_change,
+    const std::u16string& old_title,
+    const std::u16string& new_title,
+    const SkBitmap& old_icon,
+    const SkBitmap& new_icon,
+    content::WebContents* web_contents,
+    web_app::AppIdentityDialogCallback callback);
+
+// Sets whether |ShowWebAppIdentityUpdateDialog| should accept immediately
+// without any user interaction.
+void SetAutoAcceptAppIdentityUpdateForTesting(bool auto_accept);
+
 #if !defined(OS_ANDROID)
 // Callback used to indicate whether a user has accepted the launch of a
 // web app. The boolean parameter is true when the user accepts the dialog.
@@ -352,6 +376,7 @@
   EXTENSION_INSTALL_FRICTION = 108,
   FILE_HANDLING_PERMISSION_REQUEST = 109,
   SIGNIN_ENTERPRISE_INTERCEPTION = 110,
+  APP_IDENTITY_UPDATE_CONFIRMATION = 111,
   // Add values above this line with a corresponding label in
   // tools/metrics/histograms/enums.xml
   MAX_VALUE
diff --git a/chrome/browser/ui/search/DEPS b/chrome/browser/ui/search/DEPS
new file mode 100644
index 0000000..3a27932
--- /dev/null
+++ b/chrome/browser/ui/search/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  # Instant and WebUI NTPs should be independent from each other.
+  "-chrome/browser/new_tab_page"
+]
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index d854e30..f6bfdad 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -64,6 +64,7 @@
 #include "chrome/browser/sessions/session_tab_helper_factory.h"
 #include "chrome/browser/ssl/chrome_security_blocking_page_factory.h"
 #include "chrome/browser/ssl/connection_help_tab_helper.h"
+#include "chrome/browser/ssl/https_only_mode_tab_helper.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
 #include "chrome/browser/subresource_filter/chrome_content_subresource_filter_throttle_manager_factory.h"
 #include "chrome/browser/subresource_redirect/subresource_redirect_observer.h"
@@ -270,6 +271,7 @@
       web_contents, TopSitesFactory::GetForProfile(profile).get());
   HistoryTabHelper::CreateForWebContents(web_contents);
   HistoryClustersTabHelper::CreateForWebContents(web_contents);
+  HttpsOnlyModeTabHelper::CreateForWebContents(web_contents);
   webapps::InstallableManager::CreateForWebContents(web_contents);
   PrefetchProxyTabHelper::CreateForWebContents(web_contents);
   LiteVideoObserver::MaybeCreateForWebContents(web_contents);
diff --git a/chrome/browser/ui/views/apps/app_dialog/app_uninstall_dialog_view.cc b/chrome/browser/ui/views/apps/app_dialog/app_uninstall_dialog_view.cc
index 660b39e..003a10c 100644
--- a/chrome/browser/ui/views/apps/app_dialog/app_uninstall_dialog_view.cc
+++ b/chrome/browser/ui/views/apps/app_dialog/app_uninstall_dialog_view.cc
@@ -149,6 +149,7 @@
     case apps::mojom::AppType::kMacOs:
     case apps::mojom::AppType::kStandaloneBrowser:
     case apps::mojom::AppType::kRemote:
+    case apps::mojom::AppType::kStandaloneBrowserExtension:
       NOTREACHED();
       break;
     case apps::mojom::AppType::kArc:
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
index 48ea81b8..4792764 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -108,6 +108,7 @@
 #include "ui/views/animation/ink_drop_impl.h"
 #include "ui/views/animation/ink_drop_mask.h"
 #include "ui/views/button_drag_utils.h"
+#include "ui/views/cascading_property.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/button/label_button_border.h"
 #include "ui/views/controls/button/menu_button.h"
@@ -492,8 +493,8 @@
   if (browser_view)
     SetBackground(std::make_unique<TopContainerBackground>(browser_view));
 
-  views::FocusRing::SetBackgroundColorIdForSubtree(
-      this, ThemeProperties::COLOR_TOOLBAR);
+  views::SetCascadingThemeProviderColor(this, views::kCascadingBackgroundColor,
+                                        ThemeProperties::COLOR_TOOLBAR);
 }
 
 BookmarkBarView::~BookmarkBarView() {
diff --git a/chrome/browser/ui/views/download/download_shelf_view.cc b/chrome/browser/ui/views/download/download_shelf_view.cc
index d840bb6..53be988 100644
--- a/chrome/browser/ui/views/download/download_shelf_view.cc
+++ b/chrome/browser/ui/views/download/download_shelf_view.cc
@@ -41,6 +41,7 @@
 #include "ui/gfx/presentation_feedback.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/background.h"
+#include "ui/views/cascading_property.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/button/image_button_factory.h"
 #include "ui/views/controls/button/md_text_button.h"
@@ -109,8 +110,8 @@
   // and return to chrome with the download shelf still open.
   mouse_watcher_.set_notify_on_exit_time(base::TimeDelta::FromSeconds(5));
   SetID(VIEW_ID_DOWNLOAD_SHELF);
-  views::FocusRing::SetBackgroundColorIdForSubtree(
-      this, ThemeProperties::COLOR_TOOLBAR);
+  views::SetCascadingThemeProviderColor(this, views::kCascadingBackgroundColor,
+                                        ThemeProperties::COLOR_TOOLBAR);
 }
 
 DownloadShelfView::~DownloadShelfView() = default;
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.cc
index 77bb5dd..78decab 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "base/containers/cxx20_erase.h"
 #include "base/metrics/user_metrics.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
@@ -424,6 +425,24 @@
   return !should_leave_to_top_container;
 }
 
+views::View::Views BrowserNonClientFrameViewChromeOS::GetChildrenInZOrder() {
+  views::View::Views paint_order =
+      BrowserNonClientFrameView::GetChildrenInZOrder();
+  views::ClientView* client_view =
+      GetWidget() ? GetWidget()->client_view() : nullptr;
+
+  // Move the client view in front of the frame animator view added by the frame
+  // header helper, if present. This allows the frame animator view to still be
+  // at the bottom of the z-order while also keeping the rest of the frame
+  // view's children on top of the client view.
+  if (frame()->ShouldDrawFrameHeader() && client_view &&
+      base::Erase(paint_order, client_view)) {
+    paint_order.insert(std::next(paint_order.begin(), 1), client_view);
+  }
+
+  return paint_order;
+}
+
 SkColor BrowserNonClientFrameViewChromeOS::GetTitleColor() {
   return browser_view()->GetRegularOrGuestSession()
              ? kNormalWindowTitleTextColor
@@ -584,12 +603,20 @@
 }
 
 void BrowserNonClientFrameViewChromeOS::OnImmersiveRevealEnded() {
-  // Ensure the WebAppFrameToolbarView and FrameCaptionButtonContainerView
-  // receive events before the BrowserView by appending instead of inserting
-  // the child views.
-  if (web_app_frame_toolbar())
-    AddChildView(web_app_frame_toolbar());
-  AddChildView(caption_button_container_);
+  AddChildViewAt(caption_button_container_, 0);
+
+  if (web_app_frame_toolbar()) {
+    views::ClientView* client_view =
+        GetWidget() ? GetWidget()->client_view() : nullptr;
+
+    // Add the web app frame toolbar at the end, but before the client view if
+    // it exists.
+    if (client_view && GetIndexOf(client_view) >= 0) {
+      AddChildViewAt(web_app_frame_toolbar(), GetIndexOf(client_view));
+    } else {
+      AddChildView(web_app_frame_toolbar());
+    }
+  }
   Layout();
 }
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.h
index f7772e5..b80e172 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.h
@@ -81,6 +81,7 @@
   void ChildPreferredSizeChanged(views::View* child) override;
   bool DoesIntersectRect(const views::View* target,
                          const gfx::Rect& rect) const override;
+  views::View::Views GetChildrenInZOrder() override;
 
   // BrowserFrameHeaderChromeOS::AppearanceProvider:
   SkColor GetTitleColor() override;
diff --git a/chrome/browser/ui/views/frame/tab_strip_region_view.cc b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
index a621be042..bd626f3 100644
--- a/chrome/browser/ui/views/frame/tab_strip_region_view.cc
+++ b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
@@ -24,6 +24,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/views/cascading_property.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/layout/flex_layout_types.h"
@@ -49,8 +50,9 @@
 }  // namespace
 
 TabStripRegionView::TabStripRegionView(std::unique_ptr<TabStrip> tab_strip) {
-  views::FocusRing::SetBackgroundColorIdForSubtree(
-      this, ThemeProperties::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_INACTIVE);
+  views::SetCascadingThemeProviderColor(
+      this, views::kCascadingBackgroundColor,
+      ThemeProperties::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_INACTIVE);
 
   layout_manager_ = SetLayoutManager(std::make_unique<views::FlexLayout>());
   layout_manager_->SetOrientation(views::LayoutOrientation::kHorizontal);
diff --git a/chrome/browser/ui/views/infobars/infobar_container_view.cc b/chrome/browser/ui/views/infobars/infobar_container_view.cc
index 7b4e19af..ede660d 100644
--- a/chrome/browser/ui/views/infobars/infobar_container_view.cc
+++ b/chrome/browser/ui/views/infobars/infobar_container_view.cc
@@ -21,6 +21,7 @@
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/skia_paint_util.h"
 #include "ui/views/bubble/bubble_border.h"
+#include "ui/views/cascading_property.h"
 #include "ui/views/controls/focus_ring.h"
 
 namespace {
@@ -65,8 +66,8 @@
       content_shadow_(new ContentShadow()) {
   SetID(VIEW_ID_INFO_BAR_CONTAINER);
   AddChildView(content_shadow_);
-  views::FocusRing::SetBackgroundColorIdForSubtree(
-      this, ThemeProperties::COLOR_TOOLBAR);
+  views::SetCascadingThemeProviderColor(this, views::kCascadingBackgroundColor,
+                                        ThemeProperties::COLOR_TOOLBAR);
 }
 
 InfoBarContainerView::~InfoBarContainerView() {
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
index 7301f0f..5a1907f 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
@@ -31,6 +31,7 @@
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/gfx/image/image.h"
 #include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/cascading_property.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/widget/widget.h"
 
@@ -175,8 +176,9 @@
             base::Unretained(this)));
   }
 
-  views::FocusRing::SetBackgroundColorIdForSubtree(
-      this, ThemeProperties::COLOR_OMNIBOX_RESULTS_BG);
+  views::SetCascadingThemeProviderColor(
+      this, views::kCascadingBackgroundColor,
+      ThemeProperties::COLOR_OMNIBOX_RESULTS_BG);
 }
 
 OmniboxPopupContentsView::~OmniboxPopupContentsView() {
diff --git a/chrome/browser/ui/views/payments/payment_method_view_controller.cc b/chrome/browser/ui/views/payments/payment_method_view_controller.cc
index 20bcb9d..b523653 100644
--- a/chrome/browser/ui/views/payments/payment_method_view_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_method_view_controller.cc
@@ -29,6 +29,7 @@
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/views/border.h"
+#include "ui/views/cascading_property.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/button/md_text_button.h"
 #include "ui/views/layout/box_layout.h"
@@ -40,24 +41,6 @@
 
 namespace {
 
-class MissingInfoLabel : public views::Label {
- public:
-  METADATA_HEADER(MissingInfoLabel);
-
-  MissingInfoLabel(const std::u16string& text, int text_context)
-      : Label(text, text_context) {}
-
-  // views::Label:
-  void OnThemeChanged() override {
-    Label::OnThemeChanged();
-    SetEnabledColor(GetNativeTheme()->GetSystemColor(
-        ui::NativeTheme::kColorId_LinkEnabled));
-  }
-};
-
-BEGIN_METADATA(MissingInfoLabel, views::Label)
-END_METADATA
-
 class PaymentMethodListItem : public PaymentRequestItemList::Item {
  public:
   // Does not take ownership of |app|, which should not be null and should
@@ -143,8 +126,12 @@
     std::u16string missing_info;
     if (!app_->IsCompleteForPayment()) {
       missing_info = app_->GetMissingInfoLabel();
-      card_info_container->AddChildView(std::make_unique<MissingInfoLabel>(
-          missing_info, CONTEXT_DIALOG_BODY_TEXT_SMALL));
+      views::Label* const label =
+          card_info_container->AddChildView(std::make_unique<views::Label>(
+              missing_info, CONTEXT_DIALOG_BODY_TEXT_SMALL));
+      views::SetCascadingNativeThemeColor(
+          label, views::kCascadingLabelEnabledColor,
+          ui::NativeTheme::kColorId_LinkEnabled);
     }
 
     *accessible_content = l10n_util::GetStringFUTF16(
diff --git a/chrome/browser/ui/views/profiles/profile_picker_interactive_uitest.cc b/chrome/browser/ui/views/profiles/profile_picker_interactive_uitest.cc
index e6d2c405..fd951a8 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_interactive_uitest.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_interactive_uitest.cc
@@ -183,8 +183,14 @@
 }
 
 // Checks that the signin web view is able to process keyboard events.
+// TODO(https://crbug.com/1227800): Flaky on linux.
+#if defined(OS_LINUX)
+#define MAYBE_CloseSigninWithKeyboard DISABLED_CloseSigninWithKeyboardd
+#else
+#define MAYBE_CloseSigninWithKeyboard CloseSigninWithKeyboard
+#endif
 IN_PROC_BROWSER_TEST_F(ProfilePickerInteractiveUiTest,
-                       CloseSigninWithKeyboard) {
+                       MAYBE_CloseSigninWithKeyboard) {
   ShowAndFocusPicker(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile);
 
   // Simulate a click on the signin button.
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
index 73dd1fc..83b25192 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
@@ -247,6 +247,10 @@
 
   views::BubbleDialogDelegateView::CreateBubble(this);
   set_adjust_if_offscreen(true);
+  // Ensure the hover card Widget assumes the highest z-order to avoid occlusion
+  // by other secondary UI Widgets (such as the omnibox Widget, see
+  // crbug.com/1226536).
+  GetWidget()->StackAtTop();
 
   constexpr int kFootnoteVerticalMargin = 8;
   GetBubbleFrameView()->SetFootnoteMargins(
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 8aec4b2..28d845f 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -89,6 +89,7 @@
 #include "ui/gfx/range/range.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/cascading_property.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/masked_targeter_delegate.h"
@@ -1118,8 +1119,8 @@
   // TODO(pbos): This is probably incorrect, the background of individual tabs
   // depend on their selected state. This should probably be pushed down into
   // tabs.
-  views::FocusRing::SetBackgroundColorIdForSubtree(
-      this, ThemeProperties::COLOR_TOOLBAR);
+  views::SetCascadingThemeProviderColor(this, views::kCascadingBackgroundColor,
+                                        ThemeProperties::COLOR_TOOLBAR);
   Init();
   SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
 }
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.cc b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.cc
index 93d98b8..9613dbc0 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.cc
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.cc
@@ -195,6 +195,13 @@
       views::DISTANCE_BUBBLE_PREFERRED_WIDTH));
   set_margins(gfx::Insets(0));
   SetEnableArrowKeyTraversal(true);
+  // Previous role is kUnknown. This override makes ChromeVox draw accessibility
+  // focus on the entire content area instead of the first combobox. We are not
+  // using BubbleDialogDelegate::GetAccessibleWindowRole() which will return
+  // kAlertDialog for this case. kAlertDialog will tell screen readers to
+  // announce all contents of the bubble when it opens and previous
+  // accessibility feedback said that behavior was confusing.
+  GetViewAccessibility().OverrideRole(ax::mojom::Role::kDialog);
 
 // TODO(elainechien): Take care of additional cases 1) kSafeMode switch is
 // present 2) user is secondary user.
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index f2c5304..9fe38374 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -91,6 +91,7 @@
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/native_theme/native_theme_aura.h"
 #include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/cascading_property.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/view_class_properties.h"
@@ -176,8 +177,8 @@
     for (const auto& view_and_command : GetViewCommandMap())
       chrome::AddCommandObserver(browser_, view_and_command.second, this);
   }
-  views::FocusRing::SetBackgroundColorIdForSubtree(
-      this, ThemeProperties::COLOR_TOOLBAR);
+  views::SetCascadingThemeProviderColor(this, views::kCascadingBackgroundColor,
+                                        ThemeProperties::COLOR_TOOLBAR);
 }
 
 ToolbarView::~ToolbarView() {
diff --git a/chrome/browser/ui/views/web_apps/web_app_identity_update_confirmation_view.cc b/chrome/browser/ui/views/web_apps/web_app_identity_update_confirmation_view.cc
new file mode 100644
index 0000000..028929a
--- /dev/null
+++ b/chrome/browser/ui/views/web_apps/web_app_identity_update_confirmation_view.cc
@@ -0,0 +1,222 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/web_apps/web_app_identity_update_confirmation_view.h"
+
+#include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.h"
+#include "chrome/browser/web_applications/components/web_app_callback_app_identity.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/constrained_window/constrained_window_views.h"
+#include "components/webapps/browser/installable/installable_metrics.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/grid_layout.h"
+#include "ui/views/style/typography.h"
+
+namespace {
+const int kArrowIconSize = 32;
+
+bool g_auto_accept_app_identity_update_for_testing = false;
+
+}  // namespace
+
+WebAppIdentityUpdateConfirmationView::~WebAppIdentityUpdateConfirmationView() =
+    default;
+
+WebAppIdentityUpdateConfirmationView::WebAppIdentityUpdateConfirmationView(
+    const std::string& app_id,
+    bool title_change,
+    bool icon_change,
+    const std::u16string& old_title,
+    const std::u16string& new_title,
+    const SkBitmap& old_icon,
+    const SkBitmap& new_icon,
+    content::WebContents* web_contents,
+    web_app::AppIdentityDialogCallback callback) {
+  app_id_ = app_id;
+  web_contents_ = web_contents;
+  DCHECK(!callback.is_null());
+  callback_ = std::move(callback);
+  SetButtonLabel(ui::DIALOG_BUTTON_CANCEL,
+                 l10n_util::GetStringUTF16(IDS_WEBAPP_UPDATE_NEGATIVE_BUTTON));
+  SetModalType(ui::MODAL_TYPE_WINDOW);
+  DCHECK(title_change || icon_change);
+  SetTitle(title_change
+               ? (icon_change ? IDS_WEBAPP_UPDATE_DIALOG_TITLE_NAME_AND_ICON
+                              : IDS_WEBAPP_UPDATE_DIALOG_TITLE_NAME)
+               : IDS_WEBAPP_UPDATE_DIALOG_TITLE_ICON);
+
+  SetAcceptCallback(
+      base::BindOnce(&WebAppIdentityUpdateConfirmationView::OnDialogAccepted,
+                     base::Unretained(this)));
+
+  const ChromeLayoutProvider* layout_provider = ChromeLayoutProvider::Get();
+  set_margins(layout_provider->GetDialogInsetsForContentType(
+      views::DialogContentType::kControl, views::DialogContentType::kText));
+  views::GridLayout* layout =
+      SetLayoutManager(std::make_unique<views::GridLayout>());
+
+  // The headline column set is simply a single column that fills up the row.
+  constexpr int kColumnSetIdHeadline = 0;
+  views::ColumnSet* column_set_headline =
+      layout->AddColumnSet(kColumnSetIdHeadline);
+  column_set_headline->AddColumn(
+      views::GridLayout::FILL, views::GridLayout::CENTER,
+      views::GridLayout::kFixedSize,
+      views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
+
+  // The main column set is |padding|col1|padding|arrow|padding|col2|padding|,
+  // where col1 and col2 contain the 'before' and 'after' values (either text or
+  // icon) and the first and last columns grow as needed to fill upp the rest.
+  constexpr int kColumnSetIdMain = 1;
+  views::ColumnSet* column_set_main = layout->AddColumnSet(kColumnSetIdMain);
+
+  // Padding column on the far left side of the dialog. Grows as needed to keep
+  // the views centered.
+  column_set_main->AddPaddingColumn(/*resize_percent= */ 100, /* width= */ 0);
+  // Column showing the 'before' icon/text.
+  column_set_main->AddColumn(views::GridLayout::CENTER,
+                             views::GridLayout::CENTER,
+                             views::GridLayout::kFixedSize,
+                             views::GridLayout::ColumnSize::kUsePreferred,
+                             /* fixed_width= */ 0, /* min_width= */ 0);
+  // Padding between the left side and the arrow.
+  column_set_main->AddPaddingColumn(
+      views::GridLayout::kFixedSize,
+      layout_provider->GetDistanceMetric(
+          views::DISTANCE_RELATED_CONTROL_HORIZONTAL));
+  // Column showing the arrow to indicate what is before and what is after.
+  column_set_main->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER,
+                             views::GridLayout::kFixedSize,
+                             views::GridLayout::ColumnSize::kUsePreferred,
+                             /* fixed_width= */ 0,
+                             /* min_width= */ 0);
+  // Padding between the arrow and the right side.
+  column_set_main->AddPaddingColumn(
+      views::GridLayout::FILL, layout_provider->GetDistanceMetric(
+                                   views::DISTANCE_RELATED_CONTROL_HORIZONTAL));
+  // Column showing the 'after' icon/text.
+  column_set_main->AddColumn(views::GridLayout::CENTER,
+                             views::GridLayout::CENTER,
+                             views::GridLayout::kFixedSize,
+                             views::GridLayout::ColumnSize::kUsePreferred,
+                             /* fixed_width= */ 0, /* min_width= */ 0);
+  // Padding column on the far right side of the dialog. Grows as needed to keep
+  // the views centered.
+  column_set_main->AddPaddingColumn(/*resize_percent= */ 100, /* width= */ 0);
+
+  layout->StartRow(views::GridLayout::kFixedSize, kColumnSetIdHeadline);
+
+  auto message_label = std::make_unique<views::Label>(
+      l10n_util::GetStringUTF16(IDS_WEBAPP_UPDATE_EXPLANATION),
+      views::style::CONTEXT_LABEL);
+  message_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  message_label->SetMultiLine(true);
+  layout->AddView(std::move(message_label));
+
+  layout->AddPaddingRow(
+      views::GridLayout::kFixedSize,
+      2 * layout_provider->GetDistanceMetric(DISTANCE_CONTROL_LIST_VERTICAL));
+
+  layout->StartRow(views::GridLayout::kFixedSize, kColumnSetIdMain);
+
+  auto old_icon_image_view = std::make_unique<views::ImageView>();
+  gfx::Size image_size(web_app::kWebAppIconSmall, web_app::kWebAppIconSmall);
+  old_icon_image_view->SetImageSize(image_size);
+  old_icon_image_view->SetImage(gfx::ImageSkia::CreateFrom1xBitmap(old_icon));
+  layout->AddView(std::move(old_icon_image_view));
+
+  auto arrow = std::make_unique<views::ImageView>();
+  arrow->SetImage(
+      gfx::CreateVectorIcon(kKeyboardArrowRightIcon, kArrowIconSize,
+                            GetNativeTheme()->GetSystemColor(
+                                ui::NativeTheme::kColorId_DefaultIconColor)));
+  layout->AddView(std::move(arrow));
+
+  auto new_icon_image_view = std::make_unique<views::ImageView>();
+  new_icon_image_view->SetImageSize(image_size);
+  new_icon_image_view->SetImage(gfx::ImageSkia::CreateFrom1xBitmap(new_icon));
+  layout->AddView(std::move(new_icon_image_view));
+
+  layout->AddPaddingRow(
+      views::GridLayout::kFixedSize,
+      layout_provider->GetDistanceMetric(DISTANCE_CONTROL_LIST_VERTICAL));
+
+  auto old_title_label =
+      std::make_unique<views::Label>(old_title, views::style::CONTEXT_LABEL);
+  auto new_title_label =
+      std::make_unique<views::Label>(new_title, views::style::CONTEXT_LABEL);
+  old_title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  new_title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  old_title_label->SetMultiLine(true);
+  new_title_label->SetMultiLine(true);
+
+  layout->StartRow(views::GridLayout::kFixedSize, kColumnSetIdMain);
+  layout->AddView(std::move(old_title_label));
+  layout->SkipColumns(1);
+  layout->AddView(std::move(new_title_label));
+
+  chrome::RecordDialogCreation(
+      chrome::DialogIdentifier::APP_IDENTITY_UPDATE_CONFIRMATION);
+}
+
+bool WebAppIdentityUpdateConfirmationView::ShouldShowCloseButton() const {
+  return false;
+}
+
+void WebAppIdentityUpdateConfirmationView::OnDialogAccepted() {
+  std::move(callback_).Run(web_app::AppIdentityUpdate::kAllowed);
+}
+
+bool WebAppIdentityUpdateConfirmationView::Cancel() {
+  uninstall_dialog_ = std::make_unique<WebAppUninstallDialogViews>(
+      Profile::FromBrowserContext(web_contents_->GetBrowserContext()),
+      web_contents_->GetTopLevelNativeWindow());
+  uninstall_dialog_->show_data_clearing_confirmation(false);
+  uninstall_dialog_->ConfirmUninstall(
+      app_id_, webapps::WebappUninstallSource::kAppMenu, base::DoNothing());
+  return false;
+}
+
+BEGIN_METADATA(WebAppIdentityUpdateConfirmationView, views::DialogDelegateView)
+END_METADATA
+
+namespace chrome {
+
+void ShowWebAppIdentityUpdateDialog(
+    const std::string& app_id,
+    bool title_change,
+    bool icon_change,
+    const std::u16string& old_title,
+    const std::u16string& new_title,
+    const SkBitmap& old_icon,
+    const SkBitmap& new_icon,
+    content::WebContents* web_contents,
+    web_app::AppIdentityDialogCallback callback) {
+  auto* dialog = new WebAppIdentityUpdateConfirmationView(
+      app_id, title_change, icon_change, old_title, new_title, old_icon,
+      new_icon, web_contents, std::move(callback));
+  views::Widget* dialog_widget =
+      constrained_window::CreateBrowserModalDialogViews(
+          dialog, web_contents->GetTopLevelNativeWindow());
+  dialog_widget->Show();
+
+  if (g_auto_accept_app_identity_update_for_testing) {
+    dialog->AcceptDialog();
+  }
+}
+
+void SetAutoAcceptAppIdentityUpdateForTesting(bool auto_accept) {
+  g_auto_accept_app_identity_update_for_testing = auto_accept;
+}
+
+}  // namespace chrome
diff --git a/chrome/browser/ui/views/web_apps/web_app_identity_update_confirmation_view.h b/chrome/browser/ui/views/web_apps/web_app_identity_update_confirmation_view.h
new file mode 100644
index 0000000..8926ed7
--- /dev/null
+++ b/chrome/browser/ui/views/web_apps/web_app_identity_update_confirmation_view.h
@@ -0,0 +1,61 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_IDENTITY_UPDATE_CONFIRMATION_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_IDENTITY_UPDATE_CONFIRMATION_VIEW_H_
+
+#include <string>
+
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/web_applications/components/web_app_callback_app_identity.h"
+#include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/views/window/dialog_delegate.h"
+
+class SkBitmap;
+class WebAppUninstallDialogViews;
+
+// WebAppIdentityUpdateConfirmationView provides views for showing which parts
+// of the app's identity changed so the user can make a determination whether to
+// allow the update or uninstall it.
+class WebAppIdentityUpdateConfirmationView : public views::DialogDelegateView {
+ public:
+  METADATA_HEADER(WebAppIdentityUpdateConfirmationView);
+  WebAppIdentityUpdateConfirmationView(
+      const std::string& app_id,
+      bool title_change,
+      bool icon_change,
+      const std::u16string& old_title,
+      const std::u16string& new_title,
+      const SkBitmap& old_icon,
+      const SkBitmap& new_icon,
+      content::WebContents* web_contents,
+      web_app::AppIdentityDialogCallback callback);
+  WebAppIdentityUpdateConfirmationView(
+      const WebAppIdentityUpdateConfirmationView&) = delete;
+  WebAppIdentityUpdateConfirmationView& operator=(
+      const WebAppIdentityUpdateConfirmationView&) = delete;
+  ~WebAppIdentityUpdateConfirmationView() override;
+
+ private:
+  // Overridden from views::WidgetDelegate:
+  bool ShouldShowCloseButton() const override;
+
+  // Overriden from views::DialogDelegateView:
+  bool Cancel() override;
+
+  void OnDialogAccepted();
+
+  // The id of the app whose identity is changing.
+  std::string app_id_;
+
+  // A callback to relay the results of the app identity update dialog.
+  web_app::AppIdentityDialogCallback callback_;
+
+  // The app uninstall dialog, shown to confirm the uninstallation.
+  std::unique_ptr<WebAppUninstallDialogViews> uninstall_dialog_;
+
+  content::WebContents* web_contents_;
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_IDENTITY_UPDATE_CONFIRMATION_VIEW_H_
diff --git a/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.cc b/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.cc
index e1178222..02b2b509 100644
--- a/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.cc
@@ -61,6 +61,7 @@
     Profile* profile,
     WebAppUninstallDialogViews* dialog_view,
     web_app::AppId app_id,
+    bool show_data_clearing_confirmation,
     webapps::WebappUninstallSource uninstall_source,
     std::map<SquareSizePx, SkBitmap> icon_bitmaps)
     : dialog_(dialog_view), app_id_(app_id), profile_(profile) {
@@ -70,6 +71,7 @@
   app_start_url_ = provider->registrar().GetAppStartUrl(app_id_);
   DCHECK(!app_start_url_.is_empty());
   DCHECK(app_start_url_.is_valid());
+  show_data_clearing_confirmation_ = show_data_clearing_confirmation;
 
   gfx::Size image_size{kIconSizeInDip, kIconSizeInDip};
 
@@ -109,14 +111,16 @@
       views::DialogContentType::kText, views::DialogContentType::kText);
   set_margins(insets + gfx::Insets(0, insets.left() + kIconSizeInDip, 0, 0));
 
-  std::u16string checkbox_label = l10n_util::GetStringFUTF16(
-      IDS_EXTENSION_UNINSTALL_PROMPT_REMOVE_DATA_CHECKBOX,
-      url_formatter::FormatUrlForSecurityDisplay(
-          app_start_url_, url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC));
+  if (show_data_clearing_confirmation_) {
+    std::u16string checkbox_label = l10n_util::GetStringFUTF16(
+        IDS_EXTENSION_UNINSTALL_PROMPT_REMOVE_DATA_CHECKBOX,
+        url_formatter::FormatUrlForSecurityDisplay(
+            app_start_url_, url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC));
 
-  auto checkbox = std::make_unique<views::Checkbox>(checkbox_label);
-  checkbox->SetMultiLine(true);
-  checkbox_ = AddChildView(std::move(checkbox));
+    auto checkbox = std::make_unique<views::Checkbox>(checkbox_label);
+    checkbox->SetMultiLine(true);
+    checkbox_ = AddChildView(std::move(checkbox));
+  }
 
   uninstall_source_ = uninstall_source;
 
@@ -132,14 +136,16 @@
   if (!dialog_)
     return;
 
+  bool clear_web_app_site_data =
+      !show_data_clearing_confirmation_ || checkbox_->GetChecked();
   HistogramCloseAction action =
-      checkbox_->GetChecked()
+      clear_web_app_site_data
           ? HistogramCloseAction::kUninstallAndCheckboxChecked
           : HistogramCloseAction::kUninstall;
   UMA_HISTOGRAM_ENUMERATION("Webapp.UninstallDialogAction", action);
 
   Uninstall();
-  if (checkbox_->GetChecked())
+  if (clear_web_app_site_data)
     ClearWebAppSiteData();
 }
 
@@ -263,7 +269,8 @@
   }
 
   view_ = new WebAppUninstallDialogDelegateView(
-      profile_, this, app_id_, uninstall_source, std::move(icon_bitmaps));
+      profile_, this, app_id_, show_data_clearing_confirmation_,
+      uninstall_source, std::move(icon_bitmaps));
 
   constrained_window::CreateBrowserModalDialogViews(view_, parent_)->Show();
 
diff --git a/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.h b/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.h
index 3014d07..25f37bf 100644
--- a/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.h
+++ b/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.h
@@ -44,6 +44,7 @@
       Profile* profile,
       WebAppUninstallDialogViews* dialog_view,
       web_app::AppId app_id,
+      bool show_data_clearing_confirmation,
       webapps::WebappUninstallSource uninstall_source,
       std::map<SquareSizePx, SkBitmap> icon_bitmaps);
   WebAppUninstallDialogDelegateView(const WebAppUninstallDialogDelegateView&) =
@@ -72,9 +73,13 @@
 
   // The web app we are showing the dialog for.
   const web_app::AppId app_id_;
+
   // The dialog needs start_url copy even if app gets uninstalled.
   GURL app_start_url_;
 
+  // Whether to show the 'Also clear data for this site' checkbox.
+  bool show_data_clearing_confirmation_ = true;
+
   Profile* const profile_;
 
   webapps::WebappUninstallSource uninstall_source_;
@@ -94,6 +99,13 @@
       delete;
   ~WebAppUninstallDialogViews() override;
 
+  // Whether to show the 'Also clear data for this site' checkbox. Defaults to
+  // |true|, but when set to |false| implies deletion of data for the site will
+  // occur.
+  void show_data_clearing_confirmation(bool value) {
+    show_data_clearing_confirmation_ = value;
+  }
+
   // web_app::WebAppUninstallDialog:
   void ConfirmUninstall(const web_app::AppId& app_id,
                         webapps::WebappUninstallSource uninstall_source,
@@ -139,6 +151,9 @@
   web_app::AppId app_id_;
   Profile* const profile_;
 
+  // Whether to show the 'Also clear data for this site' checkbox.
+  bool show_data_clearing_confirmation_ = true;
+
   THREAD_CHECKER(thread_checker_);
 
   base::WeakPtrFactory<WebAppUninstallDialogViews> weak_ptr_factory_{this};
diff --git a/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc b/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc
index acbea14..86082a2 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc
+++ b/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -24,10 +25,12 @@
 #include "chrome/browser/ui/web_applications/web_app_metrics.h"
 #include "chrome/browser/ui/webui/web_app_internals/web_app_internals_source.h"
 #include "chrome/browser/web_applications/components/app_registry_controller.h"
+#include "chrome/browser/web_applications/components/web_app_callback_app_identity.h"
 #include "chrome/browser/web_applications/extensions/web_app_extension_shortcut.h"
 #include "chrome/browser/web_applications/system_web_apps/system_web_app_manager.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
+#include "components/constrained_window/constrained_window_views.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
 #include "components/services/app_service/public/cpp/types_util.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
@@ -368,6 +371,21 @@
   return nullptr;
 }
 
+void WebAppUiManagerImpl::ShowWebAppIdentityUpdateDialog(
+    const std::string& app_id,
+    bool title_change,
+    bool icon_change,
+    const std::u16string& old_title,
+    const std::u16string& new_title,
+    const SkBitmap& old_icon,
+    const SkBitmap& new_icon,
+    content::WebContents* web_contents,
+    web_app::AppIdentityDialogCallback callback) {
+  chrome::ShowWebAppIdentityUpdateDialog(
+      app_id, title_change, icon_change, old_title, new_title, old_icon,
+      new_icon, web_contents, std::move(callback));
+}
+
 void WebAppUiManagerImpl::OnBrowserAdded(Browser* browser) {
   DCHECK(started_);
   if (!IsBrowserForInstalledApp(browser))
diff --git a/chrome/browser/ui/web_applications/web_app_ui_manager_impl.h b/chrome/browser/ui/web_applications/web_app_ui_manager_impl.h
index 80d8f83..95eea892 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_manager_impl.h
+++ b/chrome/browser/ui/web_applications/web_app_ui_manager_impl.h
@@ -64,6 +64,16 @@
                               bool shortcut_created) override;
   content::WebContents* NavigateExistingWindow(const AppId& app_id,
                                                const GURL& url) override;
+  void ShowWebAppIdentityUpdateDialog(
+      const std::string& app_id,
+      bool title_change,
+      bool icon_change,
+      const std::u16string& old_title,
+      const std::u16string& new_title,
+      const SkBitmap& old_icon,
+      const SkBitmap& new_icon,
+      content::WebContents* web_contents,
+      web_app::AppIdentityDialogCallback callback) override;
 
   // BrowserListObserver:
   void OnBrowserAdded(Browser* browser) override;
diff --git a/chrome/browser/ui/webui/discards/graph_dump_impl.h b/chrome/browser/ui/webui/discards/graph_dump_impl.h
index 1e0a48c..e480224 100644
--- a/chrome/browser/ui/webui/discards/graph_dump_impl.h
+++ b/chrome/browser/ui/webui/discards/graph_dump_impl.h
@@ -11,7 +11,7 @@
 #include "base/memory/ref_counted_memory.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "chrome/browser/ui/webui/discards/discards.mojom.h"
 #include "components/performance_manager/public/graph/frame_node.h"
 #include "components/performance_manager/public/graph/graph.h"
@@ -207,7 +207,7 @@
   // The favicon requests happen on the UI thread. This helper class
   // maintains the state required to do that.
   class FaviconRequestHelper;
-  using NodeId = util::IdType64<class NodeIdTag>;
+  using NodeId = base::IdType64<class NodeIdTag>;
 
   void AddNode(const performance_manager::Node* node);
   void RemoveNode(const performance_manager::Node* node);
diff --git a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
index d8fefa5..1003e89 100644
--- a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
+++ b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/safe_browsing/test_safe_browsing_blocking_page_quiet.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
 #include "chrome/browser/ssl/chrome_security_blocking_page_factory.h"
+#include "chrome/browser/ssl/https_only_mode_controller_client.h"
 #include "chrome/browser/ssl/insecure_form/insecure_form_controller_client.h"
 #include "chrome/common/buildflags.h"
 #include "chrome/common/url_constants.h"
@@ -30,6 +31,7 @@
 #include "components/safe_browsing/core/browser/db/database_manager.h"
 #include "components/security_interstitials/content/bad_clock_blocking_page.h"
 #include "components/security_interstitials/content/blocked_interception_blocking_page.h"
+#include "components/security_interstitials/content/https_only_mode_blocking_page.h"
 #include "components/security_interstitials/content/insecure_form_blocking_page.h"
 #include "components/security_interstitials/content/legacy_tls_blocking_page.h"
 #include "components/security_interstitials/content/mitm_software_blocking_page.h"
@@ -287,6 +289,15 @@
                                                      request_url));
 }
 
+std::unique_ptr<security_interstitials::HttpsOnlyModeBlockingPage>
+CreateHttpsOnlyModePage(content::WebContents* web_contents) {
+  GURL request_url("http://example.com");
+  return std::make_unique<security_interstitials::HttpsOnlyModeBlockingPage>(
+      web_contents, request_url,
+      std::make_unique<HttpsOnlyModeControllerClient>(web_contents,
+                                                      request_url));
+}
+
 std::unique_ptr<safe_browsing::SafeBrowsingBlockingPage>
 CreateSafeBrowsingBlockingPage(content::WebContents* web_contents) {
   safe_browsing::SBThreatType threat_type =
@@ -524,6 +535,8 @@
     interstitial_delegate = CreateOriginPolicyInterstitialPage(web_contents);
   } else if (path_without_query == "/insecure_form") {
     interstitial_delegate = CreateInsecureFormPage(web_contents);
+  } else if (path_without_query == "/https_only") {
+    interstitial_delegate = CreateHttpsOnlyModePage(web_contents);
   }
 
   if (path_without_query == "/quietsafebrowsing") {
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
index e809edf..72edbbf 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -12,11 +12,11 @@
 #include "base/time/time.h"
 #include "chrome/browser/buildflags.h"
 #include "chrome/browser/cart/cart_handler.h"
+#include "chrome/browser/new_tab_page/modules/drive/drive_handler.h"
+#include "chrome/browser/new_tab_page/modules/task_module/task_module_handler.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/search/drive/drive_handler.h"
 #include "chrome/browser/search/instant_service.h"
 #include "chrome/browser/search/instant_service_factory.h"
-#include "chrome/browser/search/task_module/task_module_handler.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.h"
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
index 1e39488..a1def37f 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
@@ -7,10 +7,10 @@
 
 #include "base/macros.h"
 #include "chrome/browser/cart/chrome_cart.mojom.h"
+#include "chrome/browser/new_tab_page/modules/drive/drive.mojom.h"
+#include "chrome/browser/new_tab_page/modules/task_module/task_module.mojom.h"
 #include "chrome/browser/promo_browser_command/promo_browser_command.mojom-forward.h"
-#include "chrome/browser/search/drive/drive.mojom.h"
 #include "chrome/browser/search/instant_service_observer.h"
-#include "chrome/browser/search/task_module/task_module.mojom.h"
 #if !defined(OFFICIAL_BUILD)
 #include "chrome/browser/ui/webui/new_tab_page/foo/foo.mojom.h"  // nogncheck crbug.com/1125897
 #endif
diff --git a/chrome/browser/ui/webui/new_tab_page/untrusted_source.cc b/chrome/browser/ui/webui/new_tab_page/untrusted_source.cc
index 4eb134a..8a47252 100644
--- a/chrome/browser/ui/webui/new_tab_page/untrusted_source.cc
+++ b/chrome/browser/ui/webui/new_tab_page/untrusted_source.cc
@@ -18,9 +18,9 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_data.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_data.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_service_factory.h"
 #include "chrome/browser/ui/search/ntp_user_data_logger.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/new_tab_page_resources.h"
diff --git a/chrome/browser/ui/webui/new_tab_page/untrusted_source.h b/chrome/browser/ui/webui/new_tab_page/untrusted_source.h
index 962f6d7..44db8ba 100644
--- a/chrome/browser/ui/webui/new_tab_page/untrusted_source.h
+++ b/chrome/browser/ui/webui/new_tab_page/untrusted_source.h
@@ -9,8 +9,8 @@
 #include <vector>
 
 #include "base/scoped_observation.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_service.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_service_observer.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_service.h"
+#include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_observer.h"
 #include "content/public/browser/url_data_source.h"
 
 class Profile;
diff --git a/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc b/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
index 01c441d..625861a 100644
--- a/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
+++ b/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
@@ -90,7 +90,6 @@
           kTabsChangeDelay,
           base::BindRepeating(&TabSearchPageHandler::NotifyTabsChanged,
                               base::Unretained(this)))) {
-  Observe(web_ui_->GetWebContents());
   browser_tab_strip_tracker_.Init();
 }
 
@@ -457,7 +456,7 @@
     TabStripModel* tab_strip_model,
     const TabStripModelChange& change,
     const TabStripSelectionChange& selection) {
-  if (webui_hidden_ ||
+  if (!IsWebContentsVisible() ||
       browser_tab_strip_tracker_.is_processing_initial_browsers()) {
     return;
   }
@@ -476,7 +475,7 @@
 void TabSearchPageHandler::TabChangedAt(content::WebContents* contents,
                                         int index,
                                         TabChangeType change_type) {
-  if (webui_hidden_)
+  if (!IsWebContentsVisible())
     return;
   // TODO(crbug.com/1112496): Support more values for TabChangeType and filter
   // out the changes we are not interested in.
@@ -495,19 +494,23 @@
 }
 
 void TabSearchPageHandler::NotifyTabsChanged() {
+  if (!IsWebContentsVisible())
+    return;
   page_->TabsChanged(CreateProfileData());
   debounce_timer_->Stop();
 }
 
+bool TabSearchPageHandler::IsWebContentsVisible() {
+  auto visibility = web_ui_->GetWebContents()->GetVisibility();
+  return visibility == content::Visibility::VISIBLE ||
+         visibility == content::Visibility::OCCLUDED;
+}
+
 bool TabSearchPageHandler::ShouldTrackBrowser(Browser* browser) {
   return browser->profile() == Profile::FromWebUI(web_ui_) &&
          browser->type() == Browser::Type::TYPE_NORMAL;
 }
 
-void TabSearchPageHandler::OnVisibilityChanged(content::Visibility visibility) {
-  webui_hidden_ = visibility == content::Visibility::HIDDEN;
-}
-
 void TabSearchPageHandler::SetTimerForTesting(
     std::unique_ptr<base::RetainingOneShotTimer> timer) {
   debounce_timer_ = std::move(timer);
diff --git a/chrome/browser/ui/webui/tab_search/tab_search_page_handler.h b/chrome/browser/ui/webui/tab_search/tab_search_page_handler.h
index 1b22b53..85540d9 100644
--- a/chrome/browser/ui/webui/tab_search/tab_search_page_handler.h
+++ b/chrome/browser/ui/webui/tab_search/tab_search_page_handler.h
@@ -14,7 +14,6 @@
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "chrome/browser/ui/webui/tab_search/tab_search.mojom.h"
 #include "components/sessions/core/tab_restore_service.h"
-#include "content/public/browser/web_contents_observer.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -33,8 +32,7 @@
 
 class TabSearchPageHandler : public tab_search::mojom::PageHandler,
                              public TabStripModelObserver,
-                             public BrowserTabStripTrackerDelegate,
-                             public content::WebContentsObserver {
+                             public BrowserTabStripTrackerDelegate {
  public:
   TabSearchPageHandler(
       mojo::PendingReceiver<tab_search::mojom::PageHandler> receiver,
@@ -68,8 +66,9 @@
   // BrowserTabStripTrackerDelegate:
   bool ShouldTrackBrowser(Browser* browser) override;
 
-  // content::WebContentsObserver:
-  void OnVisibilityChanged(content::Visibility visibility) override;
+  // Returns true if the WebContents hosting the WebUI is visible to the user
+  // (in either a fully visible or partially occluded state).
+  bool IsWebContentsVisible();
 
  protected:
   void SetTimerForTesting(std::unique_ptr<base::RetainingOneShotTimer> timer);
@@ -135,7 +134,6 @@
   ui::MojoBubbleWebUIController* const webui_controller_;
   BrowserTabStripTracker browser_tab_strip_tracker_{this, this};
   std::unique_ptr<base::RetainingOneShotTimer> debounce_timer_;
-  bool webui_hidden_ = false;
 
   // Tracks how many times |CloseTab()| has been evoked for the currently open
   // instance of Tab Search for logging in UMA.
diff --git a/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc b/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc
index a9779ca..8f0a30b 100644
--- a/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc
@@ -192,6 +192,11 @@
                                         base::ASCIIToUTF16(title));
   }
 
+  void HideWebContents() {
+    web_contents_->WasHidden();
+    ASSERT_FALSE(handler_->IsWebContentsVisible());
+  }
+
   testing::StrictMock<MockPage> page_;
 
  private:
@@ -505,6 +510,29 @@
   ASSERT_FALSE(IsTimerRunning());
 }
 
+// Assert that no browser -> renderer messages are sent when the WebUI is not
+// visible.
+TEST_F(TabSearchPageHandlerTest, EventsDoNotPropagatedWhenWebUIIsHidden) {
+  HideWebContents();
+  EXPECT_CALL(page_, TabsChanged(_)).Times(0);
+  EXPECT_CALL(page_, TabUpdated(_)).Times(0);
+  EXPECT_CALL(page_, TabsRemoved(_)).Times(0);
+  FireTimer();
+
+  // Inserting tabs should not cause the debounce timer to start running.
+  ASSERT_FALSE(IsTimerRunning());
+  AddTabWithTitle(browser1(), GURL(kTabUrl1), kTabName1);
+  ASSERT_FALSE(IsTimerRunning());
+
+  // Adding the following tab would usually trigger TabUpdated() for the first
+  // tab since the tab index will change from 0 to 1
+  AddTabWithTitle(browser1(), GURL(kTabUrl2), kTabName2);
+
+  // Closing a tab would usually result in a call to TabsRemoved().
+  browser1()->tab_strip_model()->CloseWebContentsAt(
+      0, TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
+}
+
 // Ensure that tab model changes in a browser with a different profile
 // will not call TabsChanged().
 TEST_F(TabSearchPageHandlerTest, TabsNotChanged) {
diff --git a/chrome/browser/usb/web_usb_detector.cc b/chrome/browser/usb/web_usb_detector.cc
index cf2647e..b35d1d1a 100644
--- a/chrome/browser/usb/web_usb_detector.cc
+++ b/chrome/browser/usb/web_usb_detector.cc
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "chrome/browser/net/referrer.h"
 #include "chrome/browser/notifications/system_notification_helper.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -81,7 +82,7 @@
   if (!web_contents)
     return GURL();
 
-  return web_contents->GetURL();
+  return web_contents->GetVisibleURL();
 }
 
 void OpenURL(const GURL& url) {
@@ -106,6 +107,9 @@
         browser_tab_strip_tracker_(absl::in_place, this, nullptr) {
     browser_tab_strip_tracker_->Init();
   }
+  WebUsbNotificationDelegate(const WebUsbNotificationDelegate&) = delete;
+  WebUsbNotificationDelegate& operator=(const WebUsbNotificationDelegate&) =
+      delete;
 
   void OnTabStripModelChanged(
       TabStripModel* tab_strip_model,
@@ -114,7 +118,7 @@
     if (tab_strip_model->empty() || !selection.active_tab_changed())
       return;
 
-    if (base::StartsWith(selection.new_contents->GetURL().spec(),
+    if (base::StartsWith(selection.new_contents->GetVisibleURL().spec(),
                          landing_page_.spec(),
                          base::CompareCase::INSENSITIVE_ASCII)) {
       // If the disposition is not already set, go ahead and set it.
@@ -171,8 +175,6 @@
   std::string notification_id_;
   WebUsbNotificationClosed disposition_;
   absl::optional<BrowserTabStripTracker> browser_tab_strip_tracker_;
-
-  DISALLOW_COPY_AND_ASSIGN(WebUsbNotificationDelegate);
 };
 
 }  // namespace
diff --git a/chrome/browser/usb/web_usb_detector.h b/chrome/browser/usb/web_usb_detector.h
index cae4e7a..5f2de1a 100644
--- a/chrome/browser/usb/web_usb_detector.h
+++ b/chrome/browser/usb/web_usb_detector.h
@@ -18,6 +18,8 @@
 class WebUsbDetector : public device::mojom::UsbDeviceManagerClient {
  public:
   WebUsbDetector();
+  WebUsbDetector(const WebUsbDetector&) = delete;
+  WebUsbDetector& operator=(const WebUsbDetector&) = delete;
   ~WebUsbDetector() override;
 
   // Initializes the WebUsbDetector.
@@ -43,8 +45,6 @@
       client_receiver_{this};
 
   base::WeakPtrFactory<WebUsbDetector> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(WebUsbDetector);
 };
 
 #endif  // CHROME_BROWSER_USB_WEB_USB_DETECTOR_H_
diff --git a/chrome/browser/usb/web_usb_detector_unittest.cc b/chrome/browser/usb/web_usb_detector_unittest.cc
index 9b2281c..9bf3118 100644
--- a/chrome/browser/usb/web_usb_detector_unittest.cc
+++ b/chrome/browser/usb/web_usb_detector_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/notifications/notification_display_service.h"
@@ -60,7 +61,9 @@
 
 class WebUsbDetectorTest : public BrowserWithTestWindowTest {
  public:
-  WebUsbDetectorTest() {}
+  WebUsbDetectorTest() = default;
+  WebUsbDetectorTest(const WebUsbDetectorTest&) = delete;
+  WebUsbDetectorTest& operator=(const WebUsbDetectorTest&) = delete;
   ~WebUsbDetectorTest() override = default;
 
   TestingProfile* CreateProfile() override {
@@ -115,9 +118,6 @@
   device::FakeUsbDeviceManager device_manager_;
   std::unique_ptr<WebUsbDetector> web_usb_detector_;
   std::unique_ptr<NotificationDisplayServiceTester> display_service_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(WebUsbDetectorTest);
 };
 
 TEST_F(WebUsbDetectorTest, UsbDeviceAddedAndRemoved) {
@@ -532,7 +532,7 @@
   EXPECT_EQ(2, tab_strip_model->count());
   content::WebContents* web_contents =
       tab_strip_model->GetWebContentsAt(tab_strip_model->active_index());
-  EXPECT_EQ(landing_page_1, web_contents->GetURL());
+  EXPECT_EQ(landing_page_1, web_contents->GetLastCommittedURL());
   EXPECT_FALSE(display_service_->GetNotification(guid_1));
   histogram_tester.ExpectUniqueSample("WebUsb.NotificationClosed", 2, 1);
 }
@@ -559,7 +559,7 @@
   EXPECT_EQ(1, tab_strip_model->count());
   content::WebContents* web_contents =
       tab_strip_model->GetWebContentsAt(tab_strip_model->active_index());
-  EXPECT_EQ(landing_page_1, web_contents->GetURL());
+  EXPECT_EQ(landing_page_1, web_contents->GetVisibleURL());
   EXPECT_FALSE(display_service_->GetNotification(guid_1));
   histogram_tester.ExpectUniqueSample("WebUsb.NotificationClosed", 2, 1);
 }
@@ -683,7 +683,7 @@
   EXPECT_EQ(1, tab_strip_model->count());
   content::WebContents* web_contents =
       tab_strip_model->GetWebContentsAt(tab_strip_model->active_index());
-  EXPECT_EQ(landing_page_1, web_contents->GetURL());
+  EXPECT_EQ(landing_page_1, web_contents->GetVisibleURL());
   EXPECT_FALSE(display_service_->GetNotification(guid_1));
   histogram_tester.ExpectUniqueSample("WebUsb.NotificationClosed", 2, 1);
 
@@ -722,7 +722,7 @@
   EXPECT_EQ(2, tab_strip_model->count());
   content::WebContents* web_contents =
       tab_strip_model->GetWebContentsAt(tab_strip_model->active_index());
-  EXPECT_EQ(landing_page_1_fuzzed, web_contents->GetURL());
+  EXPECT_EQ(landing_page_1_fuzzed, web_contents->GetLastCommittedURL());
   EXPECT_FALSE(display_service_->GetNotification(guid_1));
   histogram_tester.ExpectUniqueSample("WebUsb.NotificationClosed", 2, 1);
 }
diff --git a/chrome/browser/web_applications/components/BUILD.gn b/chrome/browser/web_applications/components/BUILD.gn
index 48193b6..1549741 100644
--- a/chrome/browser/web_applications/components/BUILD.gn
+++ b/chrome/browser/web_applications/components/BUILD.gn
@@ -37,6 +37,7 @@
     "url_handler_manager.h",
     "web_app_audio_focus_id_map.cc",
     "web_app_audio_focus_id_map.h",
+    "web_app_callback_app_identity.h",
     "web_app_chromeos_data.cc",
     "web_app_chromeos_data.h",
     "web_app_constants.cc",
diff --git a/chrome/browser/web_applications/components/web_app_callback_app_identity.h b/chrome/browser/web_applications/components/web_app_callback_app_identity.h
new file mode 100644
index 0000000..183fe33
--- /dev/null
+++ b/chrome/browser/web_applications/components/web_app_callback_app_identity.h
@@ -0,0 +1,25 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_CALLBACK_APP_IDENTITY_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_CALLBACK_APP_IDENTITY_H_
+
+#include "base/callback.h"
+
+namespace web_app {
+
+// As part of evaluating a manifest update, the flow needs to take into
+// consideration whether the app identity (name and icon) is changing and
+// whether to allow that.
+enum class AppIdentityUpdate {
+  kAllowed = 0,
+  kUninstall,
+  kSkipped,
+};
+
+using AppIdentityDialogCallback = base::OnceCallback<void(AppIdentityUpdate)>;
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_CALLBACK_APP_IDENTITY_H_
diff --git a/chrome/browser/web_applications/components/web_app_ui_manager.h b/chrome/browser/web_applications/components/web_app_ui_manager.h
index d211a57e..a4a0453 100644
--- a/chrome/browser/web_applications/components/web_app_ui_manager.h
+++ b/chrome/browser/web_applications/components/web_app_ui_manager.h
@@ -10,6 +10,7 @@
 
 #include "base/callback_forward.h"
 #include "chrome/browser/web_applications/components/os_integration_manager.h"
+#include "chrome/browser/web_applications/components/web_app_callback_app_identity.h"
 #include "chrome/browser/web_applications/components/web_app_id.h"
 #include "chrome/browser/web_applications/components/web_app_shortcut.h"
 
@@ -73,6 +74,17 @@
 
   virtual content::WebContents* NavigateExistingWindow(const AppId& app_id,
                                                        const GURL& url) = 0;
+
+  virtual void ShowWebAppIdentityUpdateDialog(
+      const std::string& app_id,
+      bool title_change,
+      bool icon_change,
+      const std::u16string& old_title,
+      const std::u16string& new_title,
+      const SkBitmap& old_icon,
+      const SkBitmap& new_icon,
+      content::WebContents* web_contents,
+      web_app::AppIdentityDialogCallback callback) = 0;
 };
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/manifest_update_manager_browsertest.cc b/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
index 00b95cc..506c9910 100644
--- a/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
+++ b/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
@@ -20,9 +20,11 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/browser_features.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/components/app_shortcut_manager.h"
 #include "chrome/browser/web_applications/components/externally_managed_app_manager.h"
@@ -544,6 +546,39 @@
             "Different app name");
 }
 
+class ManifestUpdateManagerAppIdentityBrowserTest
+    : public ManifestUpdateManagerBrowserTest {
+  base::test::ScopedFeatureList scoped_feature_list_{
+      features::kPwaUpdateDialogForNameAndIcon};
+};
+
+IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerAppIdentityBrowserTest,
+                       VerifyAppIdentityUpdatesWithDlgForUserInstalledApps) {
+  chrome::SetAutoAcceptAppIdentityUpdateForTesting(true);
+
+  constexpr char kManifestTemplate[] = R"(
+    {
+      "name": "$1",
+      "start_url": ".",
+      "scope": "/",
+      "display": "standalone",
+      "icons": $2
+    }
+  )";
+  OverrideManifest(kManifestTemplate, {"Test app name", kInstallableIconList});
+  AppId app_id = InstallWebApp();
+
+  OverrideManifest(kManifestTemplate,
+                   {"Different app name", kAnotherInstallableIconList});
+  EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
+            ManifestUpdateResult::kAppUpdated);
+  histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
+                                      ManifestUpdateResult::kAppUpdated, 1);
+  EXPECT_EQ(GetProvider().registrar().GetAppShortName(app_id),
+            "Different app name");
+  CheckShortcutInfoUpdated(app_id, kAnotherInstallableIconTopLeftColor);
+}
+
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
                        CheckIgnoresStartUrlChange) {
   constexpr char kManifestTemplate[] = R"(
diff --git a/chrome/browser/web_applications/manifest_update_task.cc b/chrome/browser/web_applications/manifest_update_task.cc
index 1ef572c..70f17eb 100644
--- a/chrome/browser/web_applications/manifest_update_task.cc
+++ b/chrome/browser/web_applications/manifest_update_task.cc
@@ -12,6 +12,7 @@
 #include "base/containers/contains.h"
 #include "base/feature_list.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_features.h"
 #include "chrome/browser/web_applications/components/app_icon_manager.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/install_manager.h"
@@ -31,13 +32,27 @@
 
 namespace web_app {
 
+struct IconDiff {
+ public:
+  IconDiff() = default;
+  explicit IconDiff(bool changes) { changes_detected = changes; }
+  IconDiff(const SkBitmap& before_icon, const SkBitmap& after_icon) {
+    changes_detected = true;
+    before = before_icon;
+    after = after_icon;
+  }
+  bool changes_detected = false;
+  SkBitmap before;
+  SkBitmap after;
+};
+
 namespace {
 
-bool HaveIconContentsChanged(
+IconDiff HaveIconContentsChanged(
     const std::map<SquareSizePx, SkBitmap>& disk_icon_bitmaps,
     const std::map<SquareSizePx, SkBitmap>& downloaded_icon_bitmaps) {
   if (downloaded_icon_bitmaps.size() != disk_icon_bitmaps.size())
-    return true;
+    return IconDiff(true);
 
   for (const std::pair<const SquareSizePx, SkBitmap>& entry :
        downloaded_icon_bitmaps) {
@@ -46,29 +61,40 @@
 
     auto it = disk_icon_bitmaps.find(size);
     if (it == disk_icon_bitmaps.end())
-      return true;
+      return IconDiff(true);
 
     const SkBitmap& disk_bitmap = it->second;
     if (!gfx::BitmapsAreEqual(downloaded_bitmap, disk_bitmap))
-      return true;
+      return IconDiff(disk_bitmap, downloaded_bitmap);
   }
 
-  return false;
+  return IconDiff(false);
 }
 
-bool HaveIconBitmapsChanged(const IconBitmaps& disk_icon_bitmaps,
-                            const IconBitmaps& downloaded_icon_bitmaps) {
-  return HaveIconContentsChanged(disk_icon_bitmaps.any,
-                                 downloaded_icon_bitmaps.any) ||
-         HaveIconContentsChanged(disk_icon_bitmaps.maskable,
-                                 downloaded_icon_bitmaps.maskable) ||
-         HaveIconContentsChanged(disk_icon_bitmaps.monochrome,
-                                 downloaded_icon_bitmaps.monochrome);
+IconDiff HaveIconBitmapsChanged(const IconBitmaps& disk_icon_bitmaps,
+                                const IconBitmaps& downloaded_icon_bitmaps) {
+  IconDiff icon_diff = HaveIconContentsChanged(disk_icon_bitmaps.any,
+                                               downloaded_icon_bitmaps.any);
+  if (icon_diff.changes_detected)
+    return icon_diff;
+
+  icon_diff = HaveIconContentsChanged(disk_icon_bitmaps.maskable,
+                                      downloaded_icon_bitmaps.maskable);
+  if (icon_diff.changes_detected)
+    return icon_diff;
+
+  icon_diff = HaveIconContentsChanged(disk_icon_bitmaps.monochrome,
+                                      downloaded_icon_bitmaps.monochrome);
+  if (icon_diff.changes_detected)
+    return icon_diff;
+
+  return IconDiff(false);
 }
 
 // Some apps, such as pre-installed apps, have been vetted and are therefore
 // considered safe and permitted to update their names.
-bool AllowNameUpdating(const AppId& app_id, const AppRegistrar& registrar) {
+bool AllowUnpromptedNameUpdate(const AppId& app_id,
+                               const AppRegistrar& registrar) {
   const WebApp* web_app = registrar.AsWebAppRegistrar()->GetAppById(app_id);
   if (!web_app)
     return false;
@@ -78,7 +104,8 @@
 // Some apps, such as pre-installed apps, have been vetted and are therefore
 // considered safe and permitted to update their icon. For others, the feature
 // flag needs to be on.
-bool AllowIconUpdating(const AppId& app_id, const AppRegistrar& registrar) {
+bool AllowUnpromptedIconUpdate(const AppId& app_id,
+                               const AppRegistrar& registrar) {
   const WebApp* web_app = registrar.AsWebAppRegistrar()->GetAppById(app_id);
   if (!web_app)
     return false;
@@ -309,7 +336,7 @@
 
   // Allow app icon updating for certain apps, or if the existing icons are
   // empty - this means the app icon download during install failed.
-  if (AllowIconUpdating(app_id_, registrar_) &&
+  if (AllowUnpromptedIconUpdate(app_id_, registrar_) &&
       web_application_info_->icon_infos !=
           registrar_.GetAppIconInfos(app_id_)) {
     return true;
@@ -371,7 +398,7 @@
     return true;
   }
 
-  if (AllowNameUpdating(app_id_, registrar_) &&
+  if (AllowUnpromptedNameUpdate(app_id_, registrar_) &&
       web_application_info_->title !=
           base::UTF8ToUTF16(registrar_.GetAppShortName(app_id_))) {
     return true;
@@ -419,7 +446,6 @@
   }
 
   stage_ = Stage::kPendingIconReadFromDisk;
-  Observe(nullptr);
   icon_manager_.ReadAllIcons(
       app_id_, base::BindOnce(&ManifestUpdateTask::OnAllIconsRead, AsWeakPtr(),
                               std::move(icons_map)));
@@ -435,19 +461,85 @@
   }
   DCHECK(web_application_info_.has_value());
 
-  // Allow app icon updating for certain apps, or if the existing icons are
-  // empty - this means the app icon download during install failed.
-  if (AllowIconUpdating(app_id_, registrar_)) {
+  if (!AllowUnpromptedNameUpdate(app_id_, registrar_) &&
+      !AllowUnpromptedIconUpdate(app_id_, registrar_) &&
+      base::FeatureList::IsEnabled(features::kPwaUpdateDialogForNameAndIcon)) {
     // This call populates the |web_application_info_| with all icon bitmap
     // data.
-    // If this data does not match what we already have on disk, then an update
-    // is necessary.
-    // TODO(https://crbug.com/1184911): Reuse this data in the web app install
-    // task.
     FilterAndResizeIconsGenerateMissing(&web_application_info_.value(),
                                         &downloaded_icons_map);
+    IconDiff icon_diff = IsUpdateNeededForIconContents(disk_icon_bitmaps);
+    std::u16string old_title =
+        base::UTF8ToUTF16(registrar_.GetAppShortName(app_id_));
+    std::u16string new_title = web_application_info_->title;
+    bool title_change = old_title != new_title;
+
+    if (title_change || icon_diff.changes_detected) {
+      SkBitmap* before_icon = nullptr;
+      SkBitmap* after_icon = nullptr;
+      if (icon_diff.changes_detected) {
+        before_icon = &icon_diff.before;
+        after_icon = &icon_diff.after;
+      } else {
+        auto it = disk_icon_bitmaps.any.find(web_app::kWebAppIconSmall);
+        if (it != disk_icon_bitmaps.any.end()) {
+          before_icon = &it->second;
+          after_icon = &it->second;
+        }
+      }
+
+      if (before_icon != nullptr && after_icon != nullptr) {
+        ui_manager_.ShowWebAppIdentityUpdateDialog(
+            app_id_, title_change, icon_diff.changes_detected, old_title,
+            new_title, *before_icon, *after_icon, web_contents(),
+            base::BindOnce(&ManifestUpdateTask::OnPostAppIdentityUpdateCheck,
+                           AsWeakPtr(), std::move(downloaded_icons_map),
+                           std::move(disk_icon_bitmaps)));
+        // Must be called after showing the dialog, to have access to
+        // web_contents() above.
+        Observe(nullptr);
+        return;
+      }
+    }
+  }
+
+  Observe(nullptr);
+
+  // App Identity Warning dialog was not needed, proceed with update check.
+  OnPostAppIdentityUpdateCheck(downloaded_icons_map, disk_icon_bitmaps,
+                               AppIdentityUpdate::kSkipped);
+}
+
+void ManifestUpdateTask::OnPostAppIdentityUpdateCheck(
+    IconsMap downloaded_icons_map,
+    IconBitmaps disk_icon_bitmaps,
+    AppIdentityUpdate app_identity_update_allowed) {
+  app_identity_update_allowed_ =
+      app_identity_update_allowed == AppIdentityUpdate::kAllowed;
+  if (app_identity_update_allowed_) {
+    UpdateAfterWindowsClose();
+    return;
+  }
+
+  // Allow app icon updating for certain apps, or if the existing icons are
+  // empty - this means the app icon download during install failed.
+  if (AllowUnpromptedIconUpdate(app_id_, registrar_)) {
+    // When kPwaUpdateDialogForNameAndIcon is enabled, the FilterAndResizeIcons
+    // call has already been made.
+    if (!base::FeatureList::IsEnabled(
+            features::kPwaUpdateDialogForNameAndIcon)) {
+      // This call populates the |web_application_info_| with all icon bitmap
+      // data.
+      // If this data does not match what we already have on disk, then an
+      // update is necessary.
+      // TODO(https://crbug.com/1184911): Reuse this data in the web app install
+      // task.
+      FilterAndResizeIconsGenerateMissing(&web_application_info_.value(),
+                                          &downloaded_icons_map);
+    }
+
     // TODO: compare in a BEST_EFFORT blocking PostTaskAndReply.
-    if (IsUpdateNeededForIconContents(disk_icon_bitmaps)) {
+    if (IsUpdateNeededForIconContents(disk_icon_bitmaps).changes_detected) {
       UpdateAfterWindowsClose();
       return;
     }
@@ -475,7 +567,7 @@
   }
 }
 
-bool ManifestUpdateTask::IsUpdateNeededForIconContents(
+IconDiff ManifestUpdateTask::IsUpdateNeededForIconContents(
     const IconBitmaps& disk_icon_bitmaps) const {
   DCHECK(web_application_info_.has_value());
   return HaveIconBitmapsChanged(disk_icon_bitmaps,
@@ -511,7 +603,8 @@
     const IconBitmaps& downloaded_icon_bitmaps =
         downloaded_shortcuts_menu_icon_bitmaps[i];
     const IconBitmaps& disk_icon_bitmaps = disk_shortcuts_menu_icon_bitmaps[i];
-    if (HaveIconBitmapsChanged(disk_icon_bitmaps, downloaded_icon_bitmaps))
+    if (HaveIconBitmapsChanged(disk_icon_bitmaps, downloaded_icon_bitmaps)
+            .changes_detected)
       return true;
   }
 
@@ -551,7 +644,8 @@
 
   DCHECK(web_application_info_.has_value());
 
-  if (!AllowNameUpdating(app_id_, registrar_)) {
+  if (!AllowUnpromptedNameUpdate(app_id_, registrar_) &&
+      !app_identity_update_allowed_) {
     // The app's name must not change due to an automatic update, except for
     // default installed apps (that have been vetted).
     // TODO(crbug.com/1088338): Provide a safe way for apps to update their
@@ -578,9 +672,10 @@
   }
 
   stage_ = Stage::kPendingMaybeReadExistingIcons;
-  // Allow app icon updating if the existing icons are empty - this means the
-  // app icon download during install failed.
-  if (AllowIconUpdating(app_id_, registrar_)) {
+  // If icon updating is disabled, then read the existing icons so they can be
+  // populated on the WebApplicationInfo.
+  if (AllowUnpromptedIconUpdate(app_id_, registrar_) ||
+      app_identity_update_allowed_) {
     OnExistingIconsRead(IconBitmaps());
     return;
   }
diff --git a/chrome/browser/web_applications/manifest_update_task.h b/chrome/browser/web_applications/manifest_update_task.h
index bf1f07541..6dfcb0c 100644
--- a/chrome/browser/web_applications/manifest_update_task.h
+++ b/chrome/browser/web_applications/manifest_update_task.h
@@ -27,6 +27,8 @@
 }
 
 namespace web_app {
+enum class AppIdentityUpdate;
+struct IconDiff;
 
 // Checks for whether file handlers have changed. Ignores differences in names,
 // which aren't stored in the apps::FileHandlers, and ordering, which may
@@ -129,7 +131,11 @@
   void OnIconsDownloaded(bool success, IconsMap icons_map);
   void OnAllIconsRead(IconsMap downloaded_icons_map,
                       IconBitmaps disk_icon_bitmaps);
-  bool IsUpdateNeededForIconContents(
+  void OnPostAppIdentityUpdateCheck(
+      IconsMap downloaded_icons_map,
+      IconBitmaps disk_icon_bitmaps,
+      AppIdentityUpdate app_identity_update_allowed);
+  IconDiff IsUpdateNeededForIconContents(
       const IconBitmaps& disk_icon_bitmaps) const;
   void OnAllShortcutsMenuIconsRead(
       ShortcutsMenuIconBitmaps disk_shortcuts_menu_icons);
@@ -160,6 +166,7 @@
   const AppId app_id_;
   StoppedCallback stopped_callback_;
   bool hang_for_testing_ = false;
+  bool app_identity_update_allowed_ = false;
 
 #if DCHECK_IS_ON()
   bool* destructor_called_ptr_ = nullptr;
diff --git a/chrome/browser/web_applications/test/test_web_app_ui_manager.h b/chrome/browser/web_applications/test/test_web_app_ui_manager.h
index 315ae7b8..a46eddc 100644
--- a/chrome/browser/web_applications/test/test_web_app_ui_manager.h
+++ b/chrome/browser/web_applications/test/test_web_app_ui_manager.h
@@ -47,6 +47,16 @@
                               bool shortcut_created) override;
   content::WebContents* NavigateExistingWindow(const AppId& app_id,
                                                const GURL& url) override;
+  void ShowWebAppIdentityUpdateDialog(
+      const std::string& app_id,
+      bool title_change,
+      bool icon_change,
+      const std::u16string& old_title,
+      const std::u16string& new_title,
+      const SkBitmap& old_icon,
+      const SkBitmap& new_icon,
+      content::WebContents* web_contents,
+      web_app::AppIdentityDialogCallback callback) override {}
 
  private:
   std::map<AppId, size_t> app_id_to_num_windows_map_;
diff --git a/chrome/chrome_cleaner/test/child_process_logger.cc b/chrome/chrome_cleaner/test/child_process_logger.cc
index a53cae55..5794aa9 100644
--- a/chrome/chrome_cleaner/test/child_process_logger.cc
+++ b/chrome/chrome_cleaner/test/child_process_logger.cc
@@ -11,6 +11,8 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 
+#include <windows.h>
+
 namespace chrome_cleaner {
 
 ChildProcessLogger::ChildProcessLogger() = default;
diff --git a/chrome/common/extensions/api/_manifest_features.json b/chrome/common/extensions/api/_manifest_features.json
index 4341e398..fdc95b5 100644
--- a/chrome/common/extensions/api/_manifest_features.json
+++ b/chrome/common/extensions/api/_manifest_features.json
@@ -56,6 +56,16 @@
     "channel": "stable",
     "extension_types": ["extension", "legacy_packaged_app"]
   },
+  "chrome_url_overrides.activationmessage": {
+    "channel": "stable",
+    "extension_types": ["extension", "legacy_packaged_app"],
+    "platforms": ["chromeos"]
+  },
+  "chrome_url_overrides.keyboard": {
+    "channel": "stable",
+    "extension_types": ["extension", "legacy_packaged_app"],
+    "platforms": ["chromeos"]
+  },
   "commands": {
     "channel": "stable",
     "extension_types": ["extension", "platform_app"]
diff --git a/chrome/common/extensions/api/api_sources.gni b/chrome/common/extensions/api/api_sources.gni
index 928dd99..9894d40 100644
--- a/chrome/common/extensions/api/api_sources.gni
+++ b/chrome/common/extensions/api/api_sources.gni
@@ -148,7 +148,11 @@
 chrome_extensions_api_schema_sources = get_path_info(schema_sources_, "abspath")
 
 chrome_extensions_manifest_only_schema_sources =
-    get_path_info([ "manifest_types.json" ], "abspath")
+    get_path_info([
+                    "manifest_types.json",
+                    "chrome_url_overrides.idl",
+                  ],
+                  "abspath")
 
 chrome_extensions_api_uncompiled_sources =
     get_path_info(uncompiled_sources_, "abspath")
diff --git a/chrome/common/extensions/api/chrome_url_overrides.idl b/chrome/common/extensions/api/chrome_url_overrides.idl
new file mode 100644
index 0000000..8ebd259
--- /dev/null
+++ b/chrome/common/extensions/api/chrome_url_overrides.idl
@@ -0,0 +1,25 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Stub namespace for the "chrome_url_overrides" manifest key.
+namespace chrome_url_overrides {
+  dictionary UrlOverrideInfo {
+    // Override for the chrome://newtab page.
+    DOMString? newtab;
+
+    // Override for the chrome://bookmarks page.
+    DOMString? bookmarks;
+
+    // Override for the chrome://history page.
+    DOMString? history;
+
+    [nodoc, platforms=("chromeos")] DOMString? activationmessage;
+    [nodoc, platforms=("chromeos")] DOMString? keyboard;
+  };
+
+  dictionary ManifestKeys {
+    // Chrome url overrides. Note that an extension can override only one page.
+    UrlOverrideInfo chrome_url_overrides;
+  };
+};
diff --git a/chrome/common/extensions/api/enterprise_reporting_private.idl b/chrome/common/extensions/api/enterprise_reporting_private.idl
index 3806c96..8a5d6a8 100644
--- a/chrome/common/extensions/api/enterprise_reporting_private.idl
+++ b/chrome/common/extensions/api/enterprise_reporting_private.idl
@@ -73,6 +73,7 @@
     boolean builtInDnsClientEnabled;
     PasswordProtectionTrigger passwordProtectionWarningTrigger;
     boolean? chromeCleanupEnabled;
+    boolean chromeRemoteDesktopAppBlocked;
   };
 
   // Invoked by <code>getContextInfo</code> to return context information.
diff --git a/chrome/common/extensions/chrome_manifest_url_handlers.cc b/chrome/common/extensions/chrome_manifest_url_handlers.cc
index d1153d96..a14a079 100644
--- a/chrome/common/extensions/chrome_manifest_url_handlers.cc
+++ b/chrome/common/extensions/chrome_manifest_url_handlers.cc
@@ -7,29 +7,20 @@
 #include <memory>
 
 #include "base/files/file_util.h"
-#include "base/lazy_instance.h"
+#include "base/no_destructor.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
-#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
-#include "chrome/common/chrome_constants.h"
-#include "chrome/common/url_constants.h"
+#include "chrome/common/extensions/api/chrome_url_overrides.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/error_utils.h"
 #include "extensions/common/file_util.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/manifest_constants.h"
 #include "extensions/common/manifest_handlers/permissions_parser.h"
-#include "extensions/common/manifest_handlers/shared_module_info.h"
 #include "extensions/common/manifest_url_handlers.h"
 #include "extensions/common/permissions/api_permission.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "ash/keyboard/ui/resources/keyboard_resource_util.h"
-#endif
-
 namespace extensions {
 
 namespace keys = manifest_keys;
@@ -38,6 +29,7 @@
 namespace {
 
 const char kOverrideExtentUrlPatternFormat[] = "chrome://%s/*";
+using ChromeUrlOverridesKeys = api::chrome_url_overrides::ManifestKeys;
 
 }  // namespace
 
@@ -47,29 +39,22 @@
 }
 }
 
-URLOverrides::URLOverrides() {
-}
-
-URLOverrides::~URLOverrides() {
-}
-
-static base::LazyInstance<URLOverrides::URLOverrideMap>::DestructorAtExit
-    g_empty_url_overrides = LAZY_INSTANCE_INITIALIZER;
+URLOverrides::URLOverrides() = default;
+URLOverrides::~URLOverrides() = default;
 
 // static
 const URLOverrides::URLOverrideMap& URLOverrides::GetChromeURLOverrides(
     const Extension* extension) {
+  static const base::NoDestructor<URLOverrides::URLOverrideMap>
+      empty_url_overrides;
   URLOverrides* url_overrides = static_cast<URLOverrides*>(
-      extension->GetManifestData(keys::kChromeURLOverrides));
+      extension->GetManifestData(ChromeUrlOverridesKeys::kChromeUrlOverrides));
   return url_overrides ? url_overrides->chrome_url_overrides_
-                       : g_empty_url_overrides.Get();
+                       : *empty_url_overrides;
 }
 
-DevToolsPageHandler::DevToolsPageHandler() {
-}
-
-DevToolsPageHandler::~DevToolsPageHandler() {
-}
+DevToolsPageHandler::DevToolsPageHandler() = default;
+DevToolsPageHandler::~DevToolsPageHandler() = default;
 
 bool DevToolsPageHandler::Parse(Extension* extension, std::u16string* error) {
   std::unique_ptr<ManifestURL> manifest_url(new ManifestURL);
@@ -98,48 +83,44 @@
   return kKeys;
 }
 
-URLOverridesHandler::URLOverridesHandler() {
-}
-
-URLOverridesHandler::~URLOverridesHandler() {
-}
+URLOverridesHandler::URLOverridesHandler() = default;
+URLOverridesHandler::~URLOverridesHandler() = default;
 
 bool URLOverridesHandler::Parse(Extension* extension, std::u16string* error) {
-  const base::DictionaryValue* overrides = NULL;
-  if (!extension->manifest()->GetDictionary(keys::kChromeURLOverrides,
-                                            &overrides)) {
-    *error = base::ASCIIToUTF16(errors::kInvalidChromeURLOverrides);
+  ChromeUrlOverridesKeys manifest_keys;
+  if (!ChromeUrlOverridesKeys::ParseFromDictionary(
+          extension->manifest()->available_values(), &manifest_keys, error)) {
     return false;
   }
-  std::unique_ptr<URLOverrides> url_overrides(new URLOverrides);
-  // Validate that the overrides are all strings
-  for (base::DictionaryValue::Iterator iter(*overrides); !iter.IsAtEnd();
-       iter.Advance()) {
-    const std::string& page = iter.key();
-    std::string val;
-    // Restrict override pages to a list of supported URLs.
-    bool is_allowed_host = page == chrome::kChromeUINewTabHost ||
-                           page == chrome::kChromeUIBookmarksHost ||
-                           page == chrome::kChromeUIHistoryHost;
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-    is_allowed_host = is_allowed_host ||
-                      page == chrome::kChromeUIActivationMessageHost ||
-                      page == keyboard::kKeyboardHost;
-#endif
 
-    if (!is_allowed_host || !iter.value().GetAsString(&val)) {
-      *error = base::ASCIIToUTF16(errors::kInvalidChromeURLOverrides);
-      return false;
-    }
+  using UrlOverrideInfo = api::chrome_url_overrides::UrlOverrideInfo;
+  auto url_overrides = std::make_unique<URLOverrides>();
+  auto property_map = std::map<const char*, const std::string*>{
+      {UrlOverrideInfo::kNewtab,
+       manifest_keys.chrome_url_overrides.newtab.get()},
+      {UrlOverrideInfo::kBookmarks,
+       manifest_keys.chrome_url_overrides.bookmarks.get()},
+      {UrlOverrideInfo::kHistory,
+       manifest_keys.chrome_url_overrides.history.get()},
+      {UrlOverrideInfo::kActivationmessage,
+       manifest_keys.chrome_url_overrides.activationmessage.get()},
+      {UrlOverrideInfo::kKeyboard,
+       manifest_keys.chrome_url_overrides.keyboard.get()}};
+
+  for (auto& property : property_map) {
+    if (!property.second)
+      continue;
+
     // Replace the entry with a fully qualified chrome-extension:// URL.
-    url_overrides->chrome_url_overrides_[page] = extension->GetResourceURL(val);
+    url_overrides->chrome_url_overrides_[property.first] =
+        extension->GetResourceURL(*property.second);
 
     // For component extensions, add override URL to extent patterns.
     if (extension->is_legacy_packaged_app() &&
         extension->location() == mojom::ManifestLocation::kComponent) {
       URLPattern pattern(URLPattern::SCHEME_CHROMEUI);
       std::string url =
-          base::StringPrintf(kOverrideExtentUrlPatternFormat, page.c_str());
+          base::StringPrintf(kOverrideExtentUrlPatternFormat, property.first);
       if (pattern.Parse(url) != URLPattern::ParseResult::kSuccess) {
         *error = ErrorUtils::FormatErrorMessageUTF16(
             errors::kInvalidURLPatternError, url);
@@ -150,18 +131,18 @@
   }
 
   // An extension may override at most one page.
-  if (overrides->DictSize() > 1) {
+  if (url_overrides->chrome_url_overrides_.size() > 1u) {
     *error = base::ASCIIToUTF16(errors::kMultipleOverrides);
     return false;
   }
 
   // If this is an NTP override extension, add the NTP override permission.
-  if (url_overrides->chrome_url_overrides_.count(chrome::kChromeUINewTabHost)) {
+  if (manifest_keys.chrome_url_overrides.newtab) {
     PermissionsParser::AddAPIPermission(
         extension, mojom::APIPermissionID::kNewTabPageOverride);
   }
 
-  extension->SetManifestData(keys::kChromeURLOverrides,
+  extension->SetManifestData(ChromeUrlOverridesKeys::kChromeUrlOverrides,
                              std::move(url_overrides));
 
   return true;
@@ -191,7 +172,8 @@
 }
 
 base::span<const char* const> URLOverridesHandler::Keys() const {
-  static constexpr const char* kKeys[] = {keys::kChromeURLOverrides};
+  static constexpr const char* kKeys[] = {
+      ChromeUrlOverridesKeys::kChromeUrlOverrides};
   return kKeys;
 }
 
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_override_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_override_unittest.cc
index 489947f..09800c7 100644
--- a/chrome/common/extensions/manifest_tests/extension_manifests_override_unittest.cc
+++ b/chrome/common/extensions/manifest_tests/extension_manifests_override_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/cxx17_backports.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/common/extensions/chrome_manifest_url_handlers.h"
 #include "chrome/common/extensions/manifest_tests/chrome_manifest_test.h"
 #include "extensions/common/manifest_constants.h"
@@ -10,15 +11,12 @@
 
 namespace errors = extensions::manifest_errors;
 
-class URLOverridesManifestTest : public ChromeManifestTest {
-};
+using URLOverridesManifestTest = ChromeManifestTest;
 
 TEST_F(URLOverridesManifestTest, Override) {
-  Testcase testcases[] = {
-    Testcase("override_newtab_and_history.json", errors::kMultipleOverrides),
-    Testcase("override_invalid_page.json", errors::kInvalidChromeURLOverrides)
-  };
-  RunTestcases(testcases, base::size(testcases), EXPECT_TYPE_ERROR);
+  RunTestcase(
+      Testcase("override_newtab_and_history.json", errors::kMultipleOverrides),
+      EXPECT_TYPE_ERROR);
 
   scoped_refptr<extensions::Extension> extension;
 
@@ -31,4 +29,22 @@
   EXPECT_EQ(extension->url().spec() + "history.html",
             extensions::URLOverrides::GetChromeURLOverrides(extension.get())
                 .find("history")->second.spec());
+
+  // An extension which specifies an invalid override should still load for
+  // future compatibility.
+  extension = LoadAndExpectSuccess("override_invalid_page.json");
+  EXPECT_TRUE(
+      extensions::URLOverrides::GetChromeURLOverrides(extension.get()).empty());
+
+  // "keyboard" property is only available on ChromeOS Ash.
+  extension = LoadAndExpectSuccess("override_keyboard_page.json");
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  EXPECT_EQ(extension->url().spec() + "a_page.html",
+            extensions::URLOverrides::GetChromeURLOverrides(extension.get())
+                .find("keyboard")
+                ->second.spec());
+#else
+  EXPECT_TRUE(
+      extensions::URLOverrides::GetChromeURLOverrides(extension.get()).empty());
+#endif
 }
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index 9943691..931e8b4 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -280,7 +280,6 @@
     deps += [
       "//components/pdf/common",
       "//components/pdf/renderer",
-      "//pdf/mojom",
     ]
 
     if (is_linux || is_chromeos) {
diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS
index 0e7ae595..3e7f88b7 100644
--- a/chrome/renderer/DEPS
+++ b/chrome/renderer/DEPS
@@ -79,7 +79,6 @@
   "+google_apis",
   "+media/base",
   "+media/mojo",
-  "+pdf/mojom/pdf.mojom.h",
   "+pdf/pdf_view_web_plugin.h",
   "+ppapi/shared_impl",
   "+services/network/public/cpp",
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 31d58aa..468b9382 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -153,6 +153,7 @@
 #include "third_party/blink/public/web/web_heap.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/public/web/web_origin_trials.h"
+#include "third_party/blink/public/web/web_plugin.h"
 #include "third_party/blink/public/web/web_plugin_container.h"
 #include "third_party/blink/public/web/web_plugin_params.h"
 #include "third_party/blink/public/web/web_security_policy.h"
@@ -207,17 +208,16 @@
 
 #if BUILDFLAG(ENABLE_PDF)
 #include "components/pdf/common/internal_plugin_helpers.h"
-#endif
+#include "components/pdf/renderer/internal_plugin_renderer_helpers.h"
 
-#if BUILDFLAG(ENABLE_PDF_UNSEASONED)
-#include "mojo/public/cpp/bindings/associated_remote.h"
-#include "pdf/mojom/pdf.mojom.h"  // nogncheck
+// TODO(crbug.com/1218971): Refactor this; only needed for
+// `chrome_pdf::PdfViewWebPlugin::PrintClient`.
 #include "pdf/pdf_view_web_plugin.h"
-#endif
-
-#if BUILDFLAG(ENABLE_PDF_UNSEASONED) && BUILDFLAG(ENABLE_PRINTING)
+#if BUILDFLAG(ENABLE_PRINTING)
 #include "chrome/renderer/chrome_pdf_view_web_plugin_print_client.h"
-#endif
+#endif  // BUILDFLAG(ENABLE_PRINTING)
+
+#endif  // BUILDFLAG(ENABLE_PDF)
 
 #if BUILDFLAG(ENABLE_PLUGINS)
 #include "chrome/common/plugin_utils.h"
@@ -1041,35 +1041,22 @@
           break;
         }
 
+#if BUILDFLAG(ENABLE_PDF)
         if (info.name ==
             ASCIIToUTF16(ChromeContentClient::kPDFInternalPluginName)) {
-          // For a PDF plugin, `params.url` holds the plugin's stream url. If
-          // `params` contains an 'original-url' attribute, reset `params.url`
-          // with its original URL value so that it can be used to determine
-          // the plugin's origin.
-          for (size_t i = 0; i < params.attribute_names.size(); ++i) {
-            if (params.attribute_names[i] == "original-url") {
-              params.url = GURL(params.attribute_values[i].Utf16());
-              break;
-            }
-          }
-
-#if BUILDFLAG(ENABLE_PDF_UNSEASONED)
-          // Create unseasoned PDF plugin directly, for development purposes.
-          // TODO(crbug.com/1123621): Implement a more permanent solution once
-          // the new PDF viewer process model is approved and in place.
-          mojo::AssociatedRemote<pdf::mojom::PdfService> pdf_service_remote;
-          render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
-              pdf_service_remote.BindNewEndpointAndPassReceiver());
-          std::unique_ptr<ChromePdfViewWebPluginPrintClient> print_client;
+          std::unique_ptr<chrome_pdf::PdfViewWebPlugin::PrintClient>
+              print_client;
 #if BUILDFLAG(ENABLE_PRINTING)
           print_client =
               std::make_unique<ChromePdfViewWebPluginPrintClient>(render_frame);
 #endif  // BUILDFLAG(ENABLE_PRINTING)
-          return new chrome_pdf::PdfViewWebPlugin(
-              std::move(pdf_service_remote), std::move(print_client), params);
-#endif  // BUILDFLAG(ENABLE_PDF_UNSEASONED)
+          WebPlugin* internal_pdf_plugin = pdf::MaybeCreateInternalPlugin(
+              render_frame, std::move(print_client), params);
+          if (internal_pdf_plugin)
+            return internal_pdf_plugin;
         }
+#endif  // BUILDFLAG(ENABLE_PDF)
+
         return render_frame->CreatePlugin(info, params);
       }
       case chrome::mojom::PluginStatus::kDisabled: {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 812402c..fcd4a53 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -5531,6 +5531,8 @@
       "../browser/metrics/desktop_session_duration/touch_mode_stats_tracker_unittest.cc",
       "../browser/metrics/tab_stats/tab_stats_data_store_unittest.cc",
       "../browser/metrics/tab_stats/tab_stats_tracker_unittest.cc",
+      "../browser/new_tab_page/one_google_bar/one_google_bar_loader_impl_unittest.cc",
+      "../browser/new_tab_page/one_google_bar/one_google_bar_service_unittest.cc",
       "../browser/page_load_metrics/observers/session_restore_page_load_metrics_observer_unittest.cc",
       "../browser/resource_coordinator/background_tab_navigation_throttle_unittest.cc",
       "../browser/resource_coordinator/decision_details_unittest.cc",
@@ -5578,8 +5580,6 @@
       "../browser/search/instant_unittest_base.cc",
       "../browser/search/instant_unittest_base.h",
       "../browser/search/most_visited_iframe_source_unittest.cc",
-      "../browser/search/one_google_bar/one_google_bar_loader_impl_unittest.cc",
-      "../browser/search/one_google_bar/one_google_bar_service_unittest.cc",
       "../browser/search/promos/promo_service_unittest.cc",
       "../browser/search/search_engine_base_url_tracker_unittest.cc",
       "../browser/search/search_suggest/search_suggest_loader_impl_unittest.cc",
@@ -5784,9 +5784,9 @@
       "../browser/cart/cart_handler_unittest.cc",
       "../browser/cart/cart_service_unittest.cc",
       "../browser/cart/fetch_discount_worker_unittest.cc",
-      "../browser/search/drive/drive_service_unittest.cc",
+      "../browser/new_tab_page/modules/drive/drive_service_unittest.cc",
+      "../browser/new_tab_page/modules/task_module/task_module_service_unittest.cc",
       "../browser/search/ntp_features_unittest.cc",
-      "../browser/search/task_module/task_module_service_unittest.cc",
 
       # Android uses a different way of showing browser windows.
       "../browser/optimization_guide/optimization_guide_tab_url_provider_unittest.cc",
diff --git a/chrome/test/data/History/push_state.html b/chrome/test/data/History/push_state.html
new file mode 100644
index 0000000..c309912
--- /dev/null
+++ b/chrome/test/data/History/push_state.html
@@ -0,0 +1,4 @@
+<meta charset="utf-8">
+<script>
+  history.pushState({'state': 123}, "Pushed title", "/pushed_url.html");
+</script>
diff --git a/chrome/test/data/extensions/api_test/scripting/css_injection/css_file2.css b/chrome/test/data/extensions/api_test/scripting/css_injection/css_file2.css
new file mode 100644
index 0000000..ea253ea
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/scripting/css_injection/css_file2.css
@@ -0,0 +1,8 @@
+/* Copyright 2021 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+body {
+  background-color: purple !important;
+  font-size: 1337px;
+}
diff --git a/chrome/test/data/extensions/api_test/scripting/css_injection/worker.js b/chrome/test/data/extensions/api_test/scripting/css_injection/worker.js
index ccd2fac..f48bd17 100644
--- a/chrome/test/data/extensions/api_test/scripting/css_injection/worker.js
+++ b/chrome/test/data/extensions/api_test/scripting/css_injection/worker.js
@@ -12,8 +12,6 @@
 const CYAN = 'rgb(0, 255, 255)';
 const YELLOW = 'rgb(255, 255, 0)';
 
-const EXACTLY_ONE_FILE_ERROR = 'Error: Exactly one file must be specified.';
-
 function getBodyColor() {
   const hostname = (new URL(location.href)).hostname;
   return hostname + ' ' + getComputedStyle(document.body).backgroundColor;
@@ -126,6 +124,36 @@
     chrome.test.succeed();
   },
 
+  async function multipleFilesSpecified() {
+    const query = {url: 'http://example.com/*'};
+    const tab = await getSingleTab(query);
+    const target = {tabId: tab.id};
+    // Inject multiple files. css_file2.css sets the background color to purple
+    // and also sets font size to 1337px. Then, css_file.css sets the background
+    // to yellow.
+    // Since stylesheets inject in-order, the end result should be that the
+    // font size is 1337px (from css_file2.css) and the background is yellow
+    // (from css_file.css, the last to run).
+    const results = await chrome.scripting.insertCSS({
+      target: target,
+      files: ['css_file2.css', 'css_file.css'],
+    });
+    chrome.test.assertEq(undefined, results);
+    const colors = await getBodyColorsForTab(tab.id);
+    chrome.test.assertEq(1, colors.length);
+    chrome.test.assertEq(`example.com ${YELLOW}`, colors[0]);
+
+    const fontSizes = await chrome.scripting.executeScript({
+      target: target,
+      func: function() { return getComputedStyle(document.body).fontSize; },
+    });
+
+    chrome.test.assertEq(1, fontSizes.length);
+    chrome.test.assertEq('1337px', fontSizes[0].result);
+
+    chrome.test.succeed();
+  },
+
   async function noSuchTab() {
     const nonExistentTabId = 99999;
     await chrome.test.assertPromiseRejects(
@@ -158,27 +186,37 @@
     const query = {url: 'http://example.com/*'};
     let tab = await getSingleTab(query);
     await chrome.test.assertPromiseRejects(
-        chrome.scripting.executeScript({
+        chrome.scripting.insertCSS({
           target: {
             tabId: tab.id,
           },
           files: [],
         }),
-        EXACTLY_ONE_FILE_ERROR);
+        'Error: At least one file must be specified.');
     chrome.test.succeed();
   },
 
-  async function multipleFilesSpecified() {
+  async function duplicateFilesSpecified() {
     const query = {url: 'http://example.com/*'};
     let tab = await getSingleTab(query);
     await chrome.test.assertPromiseRejects(
-        chrome.scripting.executeScript({
+        chrome.scripting.insertCSS({
           target: {
             tabId: tab.id,
           },
-          files: ['css_file.css', 'css_file2.css'],
+          files: ['css_file.js', 'css_file.js'],
         }),
-        EXACTLY_ONE_FILE_ERROR);
+        `Error: Duplicate file specified: 'css_file.js'.`);
+
+    // Try again with a preceding slash.
+    await chrome.test.assertPromiseRejects(
+        chrome.scripting.insertCSS({
+          target: {
+            tabId: tab.id,
+          },
+          files: ['css_file.js', '/css_file.js'],
+        }),
+        `Error: Duplicate file specified: '/css_file.js'.`);
     chrome.test.succeed();
   },
 
diff --git a/chrome/test/data/extensions/api_test/scripting/main_frame/script_file2.js b/chrome/test/data/extensions/api_test/scripting/main_frame/script_file2.js
new file mode 100644
index 0000000..aa726ed
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/scripting/main_frame/script_file2.js
@@ -0,0 +1,5 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+document.title + ' From Second Script';
diff --git a/chrome/test/data/extensions/api_test/scripting/main_frame/worker.js b/chrome/test/data/extensions/api_test/scripting/main_frame/worker.js
index 667362b..1d9aca0f 100644
--- a/chrome/test/data/extensions/api_test/scripting/main_frame/worker.js
+++ b/chrome/test/data/extensions/api_test/scripting/main_frame/worker.js
@@ -4,7 +4,6 @@
 
 const NEW_TITLE_FROM_FUNCTION = 'Hello, world!';
 const NEW_TITLE_FROM_FILE = 'Goodnight';
-const EXACTLY_ONE_FILE_ERROR = 'Error: Exactly one file must be specified.';
 
 function injectedFunction() {
   // NOTE(devlin): We currently need to (re)hard-code this title, since the
@@ -213,6 +212,28 @@
     chrome.test.succeed();
   },
 
+  async function multipleFilesSpecified() {
+    const query = {url: 'http://example.com/*'};
+    let tab = await getSingleTab(query);
+    // Double-check that the title is not the one from the script file to be
+    // injected.
+    chrome.test.assertFalse(tab.title == NEW_TITLE_FROM_FILE);
+    const results = await chrome.scripting.executeScript({
+      target: {
+        tabId: tab.id,
+      },
+      files: ['script_file.js', 'script_file2.js'],
+    });
+    // The call injected two scripts; the first changes the title, and the
+    // second reports it plus a suffix. This checks that both scripts inject
+    // and that the order was preserved (since the first sets the title used
+    // in the second).
+    chrome.test.assertEq(1, results.length);
+    chrome.test.assertEq(NEW_TITLE_FROM_FILE + ' From Second Script',
+                         results[0].result);
+    chrome.test.succeed();
+  },
+
   async function onlyOneOfFunctionAndFunc() {
     const query = {url: 'http://example.com/*'};
     let tab = await getSingleTab(query);
@@ -267,11 +288,11 @@
           },
           files: [],
         }),
-        EXACTLY_ONE_FILE_ERROR);
+        'Error: At least one file must be specified.');
     chrome.test.succeed();
   },
 
-  async function multipleFilesSpecified() {
+  async function duplicateFilesSpecified() {
     const query = {url: 'http://example.com/*'};
     let tab = await getSingleTab(query);
     await chrome.test.assertPromiseRejects(
@@ -279,9 +300,19 @@
           target: {
             tabId: tab.id,
           },
-          files: ['script_file.js', 'script_file2.js'],
+          files: ['script_file.js', 'script_file.js'],
         }),
-        EXACTLY_ONE_FILE_ERROR);
+        `Error: Duplicate file specified: 'script_file.js'.`);
+
+    // Try again with a preceding slash.
+    await chrome.test.assertPromiseRejects(
+        chrome.scripting.executeScript({
+          target: {
+            tabId: tab.id,
+          },
+          files: ['script_file.js', '/script_file.js'],
+        }),
+        `Error: Duplicate file specified: '/script_file.js'.`);
     chrome.test.succeed();
   },
 
diff --git a/chrome/test/data/extensions/api_test/scripting/remove_css/file2.css b/chrome/test/data/extensions/api_test/scripting/remove_css/file2.css
new file mode 100644
index 0000000..dd7f967b
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/scripting/remove_css/file2.css
@@ -0,0 +1,7 @@
+/* Copyright 2021 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+#main {
+  color: yellow !important;
+}
diff --git a/chrome/test/data/extensions/api_test/scripting/remove_css/worker.js b/chrome/test/data/extensions/api_test/scripting/remove_css/worker.js
index 2619899..1cf901f 100644
--- a/chrome/test/data/extensions/api_test/scripting/remove_css/worker.js
+++ b/chrome/test/data/extensions/api_test/scripting/remove_css/worker.js
@@ -20,6 +20,8 @@
 const CSS2 = CSS.replace('green', 'yellow');
 // A file to inject. This also sets the color to `INJECTED_COLOR`.
 const FILE = '/file.css';
+// A second file to inject. This sets the color to `INJECTED_COLOR2`.
+const FILE2 = '/file2.css';
 
 // Aliases for brevity.
 const insertCSS = chrome.scripting.insertCSS;
@@ -205,6 +207,39 @@
     await checkColors();
     chrome.test.succeed();
   },
+  async function insertAndRemoveCSSWithMultipleFilesShouldSucceed() {
+    // Insert two style sheets. The second should "win", since it's latest
+    // injected.
+    await insertCSS({target: {tabId}, files: [FILE, FILE2]});
+    updateExpectedState([INJECTED_COLOR2, , , , , ]);
+    await checkColors();
+
+    // Remove both previously-injected files.
+    await removeCSS({target: {tabId}, files: [FILE, FILE2]});
+    updateExpectedState([ORIGINAL_COLOR, , , , , ]);
+    await checkColors();
+
+    chrome.test.succeed();
+  },
+  async function insertMultipleFilesAndRemoveOneAtATime() {
+    // Insert two style sheets. The second should "win", since it's latest
+    // injected.
+    await insertCSS({target: {tabId}, files: [FILE, FILE2]});
+    updateExpectedState([INJECTED_COLOR2, , , , , ]);
+    await checkColors();
+
+    // Remove only one of the previously-injected files.
+    await removeCSS({target: {tabId}, files: [FILE2]});
+    updateExpectedState([INJECTED_COLOR, , , , , ]);
+    await checkColors();
+
+    // Now, remove the second.
+    await removeCSS({target: {tabId}, files: [FILE]});
+    updateExpectedState([ORIGINAL_COLOR, , , , , ]);
+    await checkColors();
+
+    chrome.test.succeed();
+  },
   async function insertCSSWithDuplicateCodeShouldSucceed() {
     // Start by inserting the second CSS (which is a different color) into the
     // top frame.
diff --git a/chrome/test/data/extensions/manifest_tests/override_keyboard_page.json b/chrome/test/data/extensions/manifest_tests/override_keyboard_page.json
new file mode 100644
index 0000000..06365212
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/override_keyboard_page.json
@@ -0,0 +1,8 @@
+{
+  "name": "test",
+  "manifest_version": 2,
+  "version": "1",
+  "chrome_url_overrides": {
+    "keyboard": "a_page.html"
+  }
+}
diff --git a/chrome/test/data/webui/cr_elements/cr_a11y_announcer_test.js b/chrome/test/data/webui/cr_elements/cr_a11y_announcer_test.js
new file mode 100644
index 0000000..728871d
--- /dev/null
+++ b/chrome/test/data/webui/cr_elements/cr_a11y_announcer_test.js
@@ -0,0 +1,64 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {CrA11yAnnouncerElement, TIMEOUT_MS} from 'chrome://resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js';
+
+import {assertEquals, assertFalse, assertNotEquals, assertTrue} from '../chai_assert.js';
+
+suite('CrA11yAnnouncerElementTest', () => {
+  setup(() => {
+    document.body.innerHTML = '';
+  });
+
+  function getMessagesDiv(announcer) {
+    return announcer.shadowRoot.querySelector('#messages');
+  }
+
+  test('CreatesAndGetsAnnouncers', () => {
+    const defaultAnnouncer = CrA11yAnnouncerElement.getInstance();
+    assertEquals(document.body, defaultAnnouncer.parentElement);
+    assertEquals(defaultAnnouncer, CrA11yAnnouncerElement.getInstance());
+
+    const dialog = document.createElement('dialog');
+    document.body.appendChild(dialog);
+    const dialogAnnouncer = CrA11yAnnouncerElement.getInstance(dialog);
+    assertEquals(dialog, dialogAnnouncer.parentElement);
+    assertEquals(dialogAnnouncer, CrA11yAnnouncerElement.getInstance(dialog));
+  });
+
+  test('QueuesMessages', async () => {
+    const announcer = CrA11yAnnouncerElement.getInstance();
+    const messagesDiv = announcer.shadowRoot.querySelector('#messages');
+
+    // Queue up 2 messages at once, and assert they both exist.
+    const message1 = 'Knock knock!';
+    const message2 = 'Who\'s there?';
+    announcer.announce(message1);
+    announcer.announce(message2);
+    await new Promise(resolve => setTimeout(resolve, TIMEOUT_MS));
+    assertTrue(messagesDiv.textContent.includes(message1));
+    assertTrue(messagesDiv.textContent.includes(message2));
+
+    // Queue up 1 message, and assert it clears out previous messages.
+    const message3 = 'No jokes allowed';
+    announcer.announce(message3);
+    await new Promise(resolve => setTimeout(resolve, TIMEOUT_MS));
+    assertFalse(messagesDiv.textContent.includes(message1));
+    assertFalse(messagesDiv.textContent.includes(message2));
+    assertTrue(messagesDiv.textContent.includes(message3));
+  });
+
+  test('ClearsAnnouncerOnDisconnect', async () => {
+    const announcer = CrA11yAnnouncerElement.getInstance();
+    const lostMessage = 'You will never hear me.';
+    announcer.announce(lostMessage);
+    announcer.remove();
+    await new Promise(resolve => setTimeout(resolve, TIMEOUT_MS));
+    assertFalse(announcer.shadowRoot.querySelector('#messages')
+                    .textContent.includes(lostMessage));
+
+    // Creates new announcer since previous announcer is removed from instances.
+    assertNotEquals(announcer, CrA11yAnnouncerElement.getInstance());
+  });
+});
diff --git a/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js b/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js
index f6524b9..24c7068b 100644
--- a/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js
+++ b/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js
@@ -26,6 +26,18 @@
 };
 
 // eslint-disable-next-line no-var
+var CrElementsA11yAnnouncerV3Test = class extends CrElementsV3BrowserTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_a11y_announcer_test.js';
+  }
+};
+
+TEST_F('CrElementsA11yAnnouncerV3Test', 'All', function() {
+  mocha.run();
+});
+
+// eslint-disable-next-line no-var
 var CrElementsButtonV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
diff --git a/chrome/test/data/webui/read_later/side_panel/bookmark_folder_test.js b/chrome/test/data/webui/read_later/side_panel/bookmark_folder_test.js
index c32ce81..237ccba4 100644
--- a/chrome/test/data/webui/read_later/side_panel/bookmark_folder_test.js
+++ b/chrome/test/data/webui/read_later/side_panel/bookmark_folder_test.js
@@ -124,4 +124,122 @@
     const url = await bookmarksApi.whenCalled('openBookmark');
     assertEquals(folder.children[1].url, url);
   });
+
+  test('MovesFocusDown', () => {
+    // No focus yet, should focus folder row.
+    assertTrue(bookmarkFolder.moveFocus(1));
+    assertEquals(
+        bookmarkFolder.shadowRoot.querySelector('.row'),
+        bookmarkFolder.shadowRoot.activeElement);
+
+    // Move focus down one, should focus first child which is a folder.
+    assertTrue(bookmarkFolder.moveFocus(1));
+    assertEquals(
+        bookmarkFolder.shadowRoot.querySelector('#children bookmark-folder'),
+        bookmarkFolder.shadowRoot.activeElement);
+
+    const bookmarkElements =
+        bookmarkFolder.shadowRoot.querySelectorAll('#children .row');
+    // Move focus down one, should focus second child, the first bookmark.
+    assertTrue(bookmarkFolder.moveFocus(1));
+    assertEquals(bookmarkElements[0], bookmarkFolder.shadowRoot.activeElement);
+
+    // Move focus down one, should focus second child, the second bookmark.
+    assertTrue(bookmarkFolder.moveFocus(1));
+    assertEquals(bookmarkElements[1], bookmarkFolder.shadowRoot.activeElement);
+
+    // No more room.
+    assertFalse(bookmarkFolder.moveFocus(1));
+  });
+
+  test('MovesFocusUp', () => {
+    // No focus yet, should focus last bookmark.
+    const bookmarkElements =
+        bookmarkFolder.shadowRoot.querySelectorAll('#children .row');
+    assertTrue(bookmarkFolder.moveFocus(-1));
+    assertEquals(
+        bookmarkElements[bookmarkElements.length - 1],
+        bookmarkFolder.shadowRoot.activeElement);
+
+    // Move focus up one, should focus the first bookmark.
+    assertTrue(bookmarkFolder.moveFocus(-1));
+    assertEquals(bookmarkElements[0], bookmarkFolder.shadowRoot.activeElement);
+
+    // Move focus up one, should focus the child folder.
+    assertTrue(bookmarkFolder.moveFocus(-1));
+    assertEquals(
+        bookmarkFolder.shadowRoot.querySelector('#children bookmark-folder'),
+        bookmarkFolder.shadowRoot.activeElement);
+
+    // Move focus up one, should focus the folder itself.
+    assertTrue(bookmarkFolder.moveFocus(-1));
+    assertEquals(
+        bookmarkFolder.shadowRoot.querySelector('.row'),
+        bookmarkFolder.shadowRoot.activeElement);
+
+    // No more room.
+    assertFalse(bookmarkFolder.moveFocus(-1));
+  });
+
+  test('DoesNotFocusHiddenChildren', async () => {
+    bookmarkFolder.openFolders = [];
+    await waitAfterNextRender();
+    assertTrue(bookmarkFolder.moveFocus(1));   // Moves focus to folder.
+    assertFalse(bookmarkFolder.moveFocus(1));  // No children to move focus to.
+  });
+
+  test('MovesFocusWithinNestedFolders', async () => {
+    bookmarkFolder.folder = {
+      id: '0',
+      title: 'Bookmarks bar',
+      children: [{
+        id: '1',
+        title: 'Nested folder 1',
+        children: [{
+          id: '2',
+          title: 'Nested folder 2',
+          children: [{
+            id: '3',
+            title: 'Nested folder 3',
+            children: [],
+          }],
+        }],
+      }],
+    };
+    bookmarkFolder.openFolders = ['0', '1', '2', '3'];
+    await waitAfterNextRender();
+
+    // Move focus down 1, should focus root folder.
+    assertTrue(bookmarkFolder.moveFocus(1));
+    assertEquals(
+        bookmarkFolder.shadowRoot.querySelector('.row'),
+        bookmarkFolder.shadowRoot.activeElement);
+
+    // Move focus down 1, should focus first nested folder.
+    assertTrue(bookmarkFolder.moveFocus(1));
+    assertEquals(
+        bookmarkFolder.folder.children[0],
+        bookmarkFolder.shadowRoot.activeElement.folder);
+
+    // Move focus down 1, should focus grandchild folder.
+    assertTrue(bookmarkFolder.moveFocus(1));
+    assertEquals(
+        bookmarkFolder.folder.children[0].children[0],
+        bookmarkFolder.shadowRoot.activeElement.shadowRoot.activeElement
+            .folder);
+
+    // Move focus down 1, should focus great grandchild folder.
+    assertTrue(bookmarkFolder.moveFocus(1));
+    assertEquals(
+        bookmarkFolder.folder.children[0].children[0].children[0],
+        bookmarkFolder.shadowRoot.activeElement.shadowRoot.activeElement
+            .shadowRoot.activeElement.folder);
+
+    // Move focus up 1, should focus grandchild folder.
+    assertTrue(bookmarkFolder.moveFocus(-1));
+    assertEquals(
+        bookmarkFolder.folder.children[0].children[0],
+        bookmarkFolder.shadowRoot.activeElement.shadowRoot.activeElement
+            .folder);
+  });
 });
\ No newline at end of file
diff --git a/chrome/test/data/webui/read_later/side_panel/bookmarks_list_test.js b/chrome/test/data/webui/read_later/side_panel/bookmarks_list_test.js
index f71d330..a21f72ae 100644
--- a/chrome/test/data/webui/read_later/side_panel/bookmarks_list_test.js
+++ b/chrome/test/data/webui/read_later/side_panel/bookmarks_list_test.js
@@ -199,4 +199,49 @@
         JSON.stringify(['5001']),
         window.localStorage[LOCAL_STORAGE_OPEN_FOLDERS_KEY]);
   });
-});
\ No newline at end of file
+
+  test('MovesFocusBetweenFolders', () => {
+    const folderElements = getFolderElements(bookmarksList);
+
+    /** @param {string} key */
+    function dispatchArrowKey(key) {
+      bookmarksList.dispatchEvent(new KeyboardEvent('keydown', {key}));
+    }
+
+    /** @param {number} index */
+    function assertActiveElement(index) {
+      assertEquals(
+          folderElements[index], bookmarksList.shadowRoot.activeElement);
+    }
+
+    // Move focus to the first folder.
+    folderElements[0].moveFocus(1);
+    assertActiveElement(0);
+
+    // One ArrowDown key should still keep focus in the first folder since the
+    // folder has children.
+    dispatchArrowKey('ArrowDown');
+    assertActiveElement(0);
+
+    // Two ArrowsDown to eventually make it to the second folder.
+    dispatchArrowKey('ArrowDown');
+    dispatchArrowKey('ArrowDown');
+    assertActiveElement(1);
+
+    // One ArrowsDown to eventually make it to the third folder.
+    dispatchArrowKey('ArrowDown');
+    assertActiveElement(2);
+
+    // One ArrowsDown to loop back to the first folder.
+    dispatchArrowKey('ArrowDown');
+    assertActiveElement(0);
+
+    // One ArrowUp to loop back to the last folder.
+    dispatchArrowKey('ArrowUp');
+    assertActiveElement(2);
+
+    // One ArrowUp to loop back to the second folder.
+    dispatchArrowKey('ArrowUp');
+    assertActiveElement(1);
+  });
+});
diff --git a/chromecast/browser/service_connector.h b/chromecast/browser/service_connector.h
index 5892867..8e41d25 100644
--- a/chromecast/browser/service_connector.h
+++ b/chromecast/browser/service_connector.h
@@ -5,7 +5,7 @@
 #ifndef CHROMECAST_BROWSER_SERVICE_CONNECTOR_H_
 #define CHROMECAST_BROWSER_SERVICE_CONNECTOR_H_
 
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "chromecast/common/mojom/service_connector.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -20,7 +20,7 @@
 //
 // We don't use an enum because the definition of these IDs is split across
 // public and internal sources.
-using ServiceConnectorClientId = util::IdType32<ServiceConnector>;
+using ServiceConnectorClientId = base::IdType32<ServiceConnector>;
 
 // Something in browser process itself (e.g. CastAudioManager)
 extern const ServiceConnectorClientId kBrowserProcessClientId;
diff --git a/chromecast/chromecast.gni b/chromecast/chromecast.gni
index 3e5c83b..377080c 100644
--- a/chromecast/chromecast.gni
+++ b/chromecast/chromecast.gni
@@ -132,6 +132,9 @@
 
   # True to link in alternate build targets for the Cast Media Runtime.
   enable_cast_media_runtime = false
+
+  # Indicating whether the device has display, touch screen, etc.
+  device_capabilities = ""
 }
 
 declare_args() {
diff --git a/chromecast/common/BUILD.gn b/chromecast/common/BUILD.gn
index 61c1b4e..9097932 100644
--- a/chromecast/common/BUILD.gn
+++ b/chromecast/common/BUILD.gn
@@ -137,4 +137,6 @@
   if (enable_library_cdms) {
     deps += [ "//media/cdm:cdm_paths" ]  # Needed by cast_content_client.cc
   }
+
+  defines = [ "DEVICE_CAPABILITIES=\"${device_capabilities}\"" ]
 }
diff --git a/chromecast/common/cast_content_client.cc b/chromecast/common/cast_content_client.cc
index dd29ac63..9edd377 100644
--- a/chromecast/common/cast_content_client.cc
+++ b/chromecast/common/cast_content_client.cc
@@ -12,6 +12,7 @@
 #include "base/files/file_util.h"
 #include "base/native_library.h"
 #include "base/path_service.h"
+#include "base/strings/strcat.h"
 #include "base/strings/stringprintf.h"
 #include "base/system/sys_info.h"
 #include "build/build_config.h"
@@ -154,6 +155,17 @@
   return s_cdm_info->get();
 }
 #endif  // BUILDFLAG(BUNDLE_WIDEVINE_CDM) && defined(OS_LINUX)
+
+std::string GetControlKey() {
+  std::string control_key = base::StrCat({" CrKey/", kFrozenCrKeyValue});
+  std::string device_capabilities(DEVICE_CAPABILITIES);
+  if (!device_capabilities.empty()) {
+    device_capabilities = base::StrCat({" (", device_capabilities, ")"});
+    control_key = base::StrCat({control_key, device_capabilities});
+  }
+  return control_key;
+}
+
 }  // namespace
 
 std::string GetUserAgent() {
@@ -174,8 +186,8 @@
           .c_str()
 #endif
       );
-  return content::BuildUserAgentFromOSAndProduct(os_info, product) + " CrKey/" +
-         kFrozenCrKeyValue;
+  return content::BuildUserAgentFromOSAndProduct(os_info, product) +
+         GetControlKey();
 }
 
 CastContentClient::~CastContentClient() {
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 681a37b..ed49493 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -1513,6 +1513,9 @@
       <message name="IDS_PERSONALIZATION_APP_BACK_BUTTON" desc="Aria label for the back button to return to a prior page">
         Back to <ph name="PAGE_NAME">$1<ex>Wallpaper</ex></ph>
       </message>
+      <message name="IDS_PERSONALIZATION_APP_WALLPAPER_COLLECTIONS" desc="Aria label for the wallpaper collections grid">
+        Wallpaper Collections
+      </message>
       <message name="IDS_PERSONALIZATION_APP_CURRENTLY_SET" desc="Label for the currently set user wallpaper section.">
         Currently set
       </message>
diff --git a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_WALLPAPER_COLLECTIONS.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_WALLPAPER_COLLECTIONS.png.sha1
new file mode 100644
index 0000000..f1953c47
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_WALLPAPER_COLLECTIONS.png.sha1
@@ -0,0 +1 @@
+9886a8cbb0984357b1fc02e21d4962e63972b364
\ No newline at end of file
diff --git a/chromeos/components/personalization_app/personalization_app_ui.cc b/chromeos/components/personalization_app/personalization_app_ui.cc
index cf91b7e9..162ed23 100644
--- a/chromeos/components/personalization_app/personalization_app_ui.cc
+++ b/chromeos/components/personalization_app/personalization_app_ui.cc
@@ -60,7 +60,8 @@
       {"title", IDS_PERSONALIZATION_APP_TITLE},
       {"back", IDS_PERSONALIZATION_APP_BACK_BUTTON},
       {"currentlySet", IDS_PERSONALIZATION_APP_CURRENTLY_SET},
-      {"myImagesLabel", IDS_PERSONALIZATION_APP_MY_IMAGES}};
+      {"myImagesLabel", IDS_PERSONALIZATION_APP_MY_IMAGES},
+      {"wallpaperCollections", IDS_PERSONALIZATION_APP_WALLPAPER_COLLECTIONS}};
   source->AddLocalizedStrings(kLocalizedStrings);
   source->UseStringsJs();
 }
diff --git a/chromeos/components/personalization_app/resources/trusted/local_images_element.html b/chromeos/components/personalization_app/resources/trusted/local_images_element.html
index a38ccd7..5ecf7282 100644
--- a/chromeos/components/personalization_app/resources/trusted/local_images_element.html
+++ b/chromeos/components/personalization_app/resources/trusted/local_images_element.html
@@ -2,23 +2,34 @@
   :host {
     overflow: hidden;
   }
+  #main {
+    height: 100%;
+    overflow-y: auto;
+    width: 100%;
+  }
 </style>
 <paper-spinner-lite active="[[imagesLoading_]]">
 </paper-spinner-lite>
 <!-- TODO(b/181697575) handle error cases and update error string to UI spec -->
 <p hidden$="[[!hasError_]]" id="error">error</p>
 <template is="dom-if" if="[[showImages_]]">
-  <iron-list items="[[getImages_(hidden, images_)]]" grid>
-    <template>
-      <div class="photo-container">
-        <div class="padding-fix">
-          <template is="dom-if"
-            if="[[shouldShowImage_(item, imageData_, imageDataLoading_)]]">
-            <img on-click="onClickImage_" data-id$="[[getImageKey_(item)]]"
-              src="[[getImageData_(item, imageData_)]]">
-          </template>
+  <div id="main" role="main" aria-label="[[i18n('myImagesLabel')]]">
+    <iron-list items="[[getImages_(hidden, images_)]]" grid role="listbox"
+        scroll-target="main"
+        aria-setsize$="[[getImageCount_(hidden, images_)]]">
+      <template>
+        <div class="photo-container" role="option" tabindex$="[[tabIndex]]"
+            aria-posinset$="[[getAriaIndex_(index)]]"
+            aria-selected$="[[getAriaSelected_(item)]]">
+          <div class="padding-fix">
+            <template is="dom-if"
+              if="[[shouldShowImage_(item, imageData_, imageDataLoading_)]]">
+              <img on-click="onClickImage_" data-id$="[[getImageKey_(item)]]"
+                src="[[getImageData_(item, imageData_)]]" alt="[[item.name]]">
+            </template>
+          </div>
         </div>
-      </div>
-    </template>
-  </iron-list>
+      </template>
+    </iron-list>
+  </div>
 </template>
diff --git a/chromeos/components/personalization_app/resources/trusted/local_images_element.js b/chromeos/components/personalization_app/resources/trusted/local_images_element.js
index 2fa915d..309cb8f 100644
--- a/chromeos/components/personalization_app/resources/trusted/local_images_element.js
+++ b/chromeos/components/personalization_app/resources/trusted/local_images_element.js
@@ -125,6 +125,25 @@
   }
 
   /**
+   * @param {boolean} hidden
+   * @param {!Array<!chromeos.personalizationApp.mojom.LocalImage>} images
+   * @return {number}
+   */
+  getImageCount_(hidden, images) {
+    return this.getImages_(hidden, images).length;
+  }
+
+  /**
+   * TODO(b/192975897) compare with currently selected image to return correct
+   * aria-selected attribute.
+   * @param {!chromeos.personalizationApp.mojom.LocalImage} image
+   * @return {string}
+   */
+  getAriaSelected_(image) {
+    return 'false';
+  }
+
+  /**
    * @private
    * @param {chromeos.personalizationApp.mojom.LocalImage} image
    * @param {Object<string, string>} imageData
@@ -172,6 +191,16 @@
         /** @type {!chromeos.personalizationApp.mojom.LocalImage} */ (image),
         getWallpaperProvider(), this.getStore());
   }
+
+
+  /**
+   * @private
+   * @param {number} i
+   * @return {number}
+   */
+  getAriaIndex_(i) {
+    return i + 1;
+  }
 }
 
 customElements.define(LocalImages.is, LocalImages);
diff --git a/chromeos/components/personalization_app/resources/trusted/wallpaper_collections_element.html b/chromeos/components/personalization_app/resources/trusted/wallpaper_collections_element.html
index 5f3d6b4..a4569b18 100644
--- a/chromeos/components/personalization_app/resources/trusted/wallpaper_collections_element.html
+++ b/chromeos/components/personalization_app/resources/trusted/wallpaper_collections_element.html
@@ -4,5 +4,6 @@
 <!-- TODO(b/181697575) handle error cases and update error string to UI spec -->
 <p hidden$="[[!hasError_]]" id="error">error</p>
 <iframe id="collections-iframe" frameBorder="0" hidden$="[[!showCollections_]]"
-    src="chrome-untrusted://personalization/untrusted/collections.html">
+    src="chrome-untrusted://personalization/untrusted/collections.html"
+    role="main" aria-label$="[[i18n('wallpaperCollections')]]">
 </iframe>
diff --git a/chromeos/components/personalization_app/resources/trusted/wallpaper_images_element.html b/chromeos/components/personalization_app/resources/trusted/wallpaper_images_element.html
index c958d713..1fae97e 100644
--- a/chromeos/components/personalization_app/resources/trusted/wallpaper_images_element.html
+++ b/chromeos/components/personalization_app/resources/trusted/wallpaper_images_element.html
@@ -6,5 +6,6 @@
 <!-- TODO(b/181697575) handle error cases and update error string to UI spec -->
 <p hidden$="[[!hasError_]]" id="error">error</p>
 <iframe frameBorder="0" id="images-iframe" hidden$="[[!showImages_]]"
-    src="chrome-untrusted://personalization/untrusted/images.html">
+    src="chrome-untrusted://personalization/untrusted/images.html" role="main"
+    aria-label="[[getMainAriaLabel_(collectionId, collections_)]]">
 </iframe>
diff --git a/chromeos/components/personalization_app/resources/trusted/wallpaper_images_element.js b/chromeos/components/personalization_app/resources/trusted/wallpaper_images_element.js
index 5ce0b4e1..3d558fc 100644
--- a/chromeos/components/personalization_app/resources/trusted/wallpaper_images_element.js
+++ b/chromeos/components/personalization_app/resources/trusted/wallpaper_images_element.js
@@ -45,6 +45,13 @@
       },
 
       /**
+       * @type {?Array<!chromeos.personalizationApp.mojom.WallpaperCollection>}
+       */
+      collections_: {
+        type: Array,
+      },
+
+      /**
        * @type {!Object<string,
        *     ?Array<!chromeos.personalizationApp.mojom.WallpaperImage>>}
        * @private
@@ -92,6 +99,7 @@
     super.connectedCallback();
     this.watch('images_', state => state.backdrop.images);
     this.watch('imagesLoading_', state => state.loading.images);
+    this.watch('collections_', state => state.backdrop.collections);
     this.updateFromStore();
   }
 
@@ -145,6 +153,28 @@
       sendImagesFunction(iframe.contentWindow, this.images_[collectionId]);
     }
   }
+
+  /**
+   * @private
+   * @param {string} collectionId
+   * @param {?Array<!chromeos.personalizationApp.mojom.WallpaperCollection>}
+   *     collections
+   * @return {string}
+   */
+  getMainAriaLabel_(collectionId, collections) {
+    if (!collectionId || !Array.isArray(collections)) {
+      return '';
+    }
+    const collection =
+        collections.find(collection => collection.id === collectionId);
+
+    if (!collection) {
+      console.warn('Did not find collection matching collectionId');
+      return '';
+    }
+
+    return collection.name;
+  }
 }
 
 customElements.define(WallpaperImages.is, WallpaperImages);
diff --git a/chromeos/components/personalization_app/resources/untrusted/collections_grid.html b/chromeos/components/personalization_app/resources/untrusted/collections_grid.html
index 2b825e8..f7da12a1 100644
--- a/chromeos/components/personalization_app/resources/untrusted/collections_grid.html
+++ b/chromeos/components/personalization_app/resources/untrusted/collections_grid.html
@@ -3,10 +3,12 @@
     margin: 12px 0;
   }
 </style>
-<iron-list items="[[tiles_]]" grid scroll-target="document">
+<iron-list items="[[tiles_]]" grid scroll-target="document" role="listbox"
+    aria-setsize$="[[tiles_.length]]">
   <template>
     <div class="photo-container" data-id$="[[item.id]]"
-      on-click="onCollectionClicked_">
+        on-click="onCollectionClicked_" tabindex$="[[tabIndex]]" role="option"
+        aria-posinset$="[[getAriaIndex_(index)]]" aria-selected="false">
       <div class="padding-fix">
         <template is="dom-if" if="[[item.preview]]">
           <img src="[[item.preview.url]]">
diff --git a/chromeos/components/personalization_app/resources/untrusted/collections_grid.js b/chromeos/components/personalization_app/resources/untrusted/collections_grid.js
index 5c1c13dd3..7bc3a16 100644
--- a/chromeos/components/personalization_app/resources/untrusted/collections_grid.js
+++ b/chromeos/components/personalization_app/resources/untrusted/collections_grid.js
@@ -232,6 +232,15 @@
     }
     selectCollection(window.parent, id);
   }
+
+  /**
+   * @private
+   * @param {number} i
+   * @return {number}
+   */
+  getAriaIndex_(i) {
+    return i + 1;
+  }
 }
 
 customElements.define(CollectionsGrid.is, CollectionsGrid);
diff --git a/chromeos/components/personalization_app/resources/untrusted/images_grid.html b/chromeos/components/personalization_app/resources/untrusted/images_grid.html
index 0cfb1c9..60d5a66 100644
--- a/chromeos/components/personalization_app/resources/untrusted/images_grid.html
+++ b/chromeos/components/personalization_app/resources/untrusted/images_grid.html
@@ -3,12 +3,15 @@
     margin: 12px 0;
   }
 </style>
-<iron-list scroll-target="document" grid items="[[images_]]">
+<iron-list scroll-target="document" grid items="[[images_]]" role="listbox"
+    aria-setsize$="[[images_.length]]">
   <template>
-    <div class="photo-container">
+    <div class="photo-container" tabindex$="[[tabIndex]]" role="option"
+        aria-posinset$="[[getAriaIndex_(index)]]"
+        aria-selected$="[[getAriaSelected_(item)]]">
       <div class="padding-fix">
         <img data-id$="[[item.assetId]]" src="[[item.url.url]]"
-            on-click="onImageClicked_">
+            on-click="onImageClicked_" alt$="[[getImgAlt_(item)]]">
       </div>
     </div>
   </template>
diff --git a/chromeos/components/personalization_app/resources/untrusted/images_grid.js b/chromeos/components/personalization_app/resources/untrusted/images_grid.js
index f8abe61..9e7a8917 100644
--- a/chromeos/components/personalization_app/resources/untrusted/images_grid.js
+++ b/chromeos/components/personalization_app/resources/untrusted/images_grid.js
@@ -69,6 +69,16 @@
   }
 
   /**
+   * TODO(b/192975897) compare with currently selected image to return correct
+   * aria-selected attribute.
+   * @param {!chromeos.personalizationApp.mojom.LocalImage} image
+   * @return {string}
+   */
+  getAriaSelected_(image) {
+    return 'false';
+  }
+
+  /**
    * Notify trusted code that a user clicked on an image.
    * @private
    * @param {!Event} e
@@ -77,6 +87,24 @@
     const img = e.currentTarget;
     selectImage(window.parent, BigInt(img.dataset.id));
   }
+
+  /**
+   * @private
+   * @param {!chromeos.personalizationApp.mojom.WallpaperImage} image
+   * @return {string}
+   */
+  getImgAlt_(image) {
+    return image.attribution.join(' ');
+  }
+
+  /**
+   * @private
+   * @param {number} i
+   * @return {number}
+   */
+  getAriaIndex_(i) {
+    return i + 1;
+  }
 }
 
 customElements.define(ImagesGrid.is, ImagesGrid);
diff --git a/chromeos/crosapi/mojom/app_service_types.mojom b/chromeos/crosapi/mojom/app_service_types.mojom
index 90ebe01..7745e57 100644
--- a/chromeos/crosapi/mojom/app_service_types.mojom
+++ b/chromeos/crosapi/mojom/app_service_types.mojom
@@ -69,6 +69,8 @@
   kArc,           // Android app.
   kWeb,           // Web app.
   kSystemWeb,     // System web app.
+  [MinVersion=1]
+  kStandaloneBrowserExtension,     // Extension based app.
 };
 
 // Whether an app is ready to launch, i.e. installed.
diff --git a/chromeos/crosapi/mojom/app_service_types_mojom_traits.cc b/chromeos/crosapi/mojom/app_service_types_mojom_traits.cc
index 281f2d77..91c40ac 100644
--- a/chromeos/crosapi/mojom/app_service_types_mojom_traits.cc
+++ b/chromeos/crosapi/mojom/app_service_types_mojom_traits.cc
@@ -141,6 +141,8 @@
       return crosapi::mojom::AppType::kWeb;
     case apps::mojom::AppType::kSystemWeb:
       return crosapi::mojom::AppType::kSystemWeb;
+    case apps::mojom::AppType::kStandaloneBrowserExtension:
+      return crosapi::mojom::AppType::kStandaloneBrowserExtension;
     case apps::mojom::AppType::kBuiltIn:
     case apps::mojom::AppType::kCrostini:
     case apps::mojom::AppType::kExtension:
@@ -170,6 +172,9 @@
     case crosapi::mojom::AppType::kSystemWeb:
       *output = apps::mojom::AppType::kSystemWeb;
       return true;
+    case crosapi::mojom::AppType::kStandaloneBrowserExtension:
+      *output = apps::mojom::AppType::kStandaloneBrowserExtension;
+      return true;
   }
 
   NOTREACHED();
diff --git a/chromeos/login/auth/cryptohome_authenticator.cc b/chromeos/login/auth/cryptohome_authenticator.cc
index 7c878063..6803e43d 100644
--- a/chromeos/login/auth/cryptohome_authenticator.cc
+++ b/chromeos/login/auth/cryptohome_authenticator.cc
@@ -833,7 +833,7 @@
   }
 
   safe_mode_delegate_->CheckSafeModeOwnership(
-      current_state_->user_context,
+      current_state_->user_context.GetUserIDHash(),
       base::BindOnce(&CryptohomeAuthenticator::OnOwnershipChecked, this));
   return false;
 }
diff --git a/chromeos/login/auth/safe_mode_delegate.h b/chromeos/login/auth/safe_mode_delegate.h
index 0a40a92..53b6683 100644
--- a/chromeos/login/auth/safe_mode_delegate.h
+++ b/chromeos/login/auth/safe_mode_delegate.h
@@ -10,8 +10,6 @@
 
 namespace chromeos {
 
-class UserContext;
-
 // In case when DeviceSettings get corrupted, it is not possible to rely
 // on them to determine if particular user can sign in. In that case
 // device enters "Safe Mode" where only owner can sign in (to recreate
@@ -38,9 +36,9 @@
   virtual bool IsSafeMode() = 0;
 
   // Method to be implemented in child. Have to call |callback| with boolean
-  // parameter that indicates if user in |context| can act as an owner in
-  // safe mode.
-  virtual void CheckSafeModeOwnership(const UserContext& context,
+  // parameter that indicates if user specified by |user_id_hash| can act as an
+  // owner in safe mode.
+  virtual void CheckSafeModeOwnership(const std::string& user_id_hash,
                                       IsOwnerCallback callback) = 0;
 };
 
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index c9da8beb..047a3b9 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-93-4554.0-1625477697-benchmark-93.0.4568.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-93-4554.0-1625477697-benchmark-93.0.4570.0-r1-redacted.afdo.xz
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index 2e28f97..1472f2e91 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -289,6 +289,8 @@
     "ui/address_form_label_formatter.h",
     "ui/address_phone_form_label_formatter.cc",
     "ui/address_phone_form_label_formatter.h",
+    "ui/autofill_image_fetcher.cc",
+    "ui/autofill_image_fetcher.h",
     "ui/autofill_popup_delegate.h",
     "ui/contact_form_label_formatter.cc",
     "ui/contact_form_label_formatter.h",
@@ -432,6 +434,7 @@
     "//components/feature_engagement",
     "//components/google/core/common",
     "//components/history/core/browser",
+    "//components/image_fetcher/core",
     "//components/infobars/core",
     "//components/keyed_service/core",
     "//components/language/core/browser",
@@ -748,6 +751,7 @@
     "test_utils/test_profiles.cc",
     "test_utils/test_profiles.h",
     "ui/address_combobox_model_unittest.cc",
+    "ui/autofill_image_fetcher_unittest.cc",
     "ui/country_combobox_model_unittest.cc",
     "ui/payments/card_unmask_prompt_controller_impl_unittest.cc",
     "ui/region_combobox_model_unittest.cc",
@@ -813,6 +817,8 @@
     "//build:chromeos_buildflags",
     "//components/autofill/core/common",
     "//components/feature_engagement",
+    "//components/image_fetcher/core:core",
+    "//components/image_fetcher/core:test_support",
     "//components/leveldb_proto",
     "//components/os_crypt",
     "//components/os_crypt:test_support",
@@ -850,6 +856,8 @@
     "//third_party/libphonenumber",
     "//third_party/re2:re2",
     "//ui/base",
+    "//ui/gfx:test_support",
+    "//ui/resources",
     "//url",
   ]
 
diff --git a/components/autofill/core/browser/DEPS b/components/autofill/core/browser/DEPS
index 1079b8d..d301083 100644
--- a/components/autofill/core/browser/DEPS
+++ b/components/autofill/core/browser/DEPS
@@ -77,5 +77,8 @@
   ],
   "password_requirements_spec_fetcher_unittest\.cc": [
     "+services/network/test",
-  ]
+  ],
+  "autofill_image_fetcher(_unittest)?\.cc": [
+    "+components/image_fetcher",
+  ],
 }
diff --git a/components/autofill/core/browser/autofill_profile_import_process.h b/components/autofill/core/browser/autofill_profile_import_process.h
index 64a3b72..9eb6782 100644
--- a/components/autofill/core/browser/autofill_profile_import_process.h
+++ b/components/autofill/core/browser/autofill_profile_import_process.h
@@ -7,7 +7,7 @@
 
 #include <vector>
 
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -16,7 +16,7 @@
 
 // The id of an ongoing profile import process.
 using AutofillProfileImportId =
-    ::util::IdTypeU64<class AutofillProfileImportIdMarker>;
+    ::base::IdTypeU64<class AutofillProfileImportIdMarker>;
 
 // Specifies the type of a profile form import.
 enum class AutofillProfileImportType {
diff --git a/components/autofill/core/browser/ui/autofill_image_fetcher.cc b/components/autofill/core/browser/ui/autofill_image_fetcher.cc
new file mode 100644
index 0000000..1f063536
--- /dev/null
+++ b/components/autofill/core/browser/ui/autofill_image_fetcher.cc
@@ -0,0 +1,125 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/ui/autofill_image_fetcher.h"
+
+#include "components/image_fetcher/core/image_decoder.h"
+#include "components/image_fetcher/core/image_fetcher_impl.h"
+#include "components/image_fetcher/core/request_metadata.h"
+#include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "ui/gfx/image/image.h"
+
+namespace autofill {
+
+namespace {
+constexpr char kUmaClientName[] = "AutofillImageFetcher";
+constexpr net::NetworkTrafficAnnotationTag kCardArtImageTrafficAnnotation =
+    net::DefineNetworkTrafficAnnotation("autofill_image_fetcher_card_art_image",
+                                        R"(
+      semantics {
+        sender: "Autofill Image Fetcher"
+        description:
+          "Fetches customized card art images for credit cards stored in "
+          "Chrome. Images are hosted on Google static content server, "
+          "the data source may come from third parties (credit card issuers)."
+        trigger: "When new credit card data is sent to Chrome if the card "
+          "has a related card art image, and when the credit card data in "
+          "the web database is refreshed and any card art image is missing."
+        data: "URL of the image to be fetched."
+        destination: GOOGLE_OWNED_SERVICE
+      }
+      policy {
+        cookies_allowed: NO
+        setting:
+          "Users can enable or disable this feature in Chromium settings by "
+          "toggling 'Credit cards and addresses using Google Payments', "
+          "under 'Advanced sync settings...'."
+        chrome_policy {
+          AutoFillEnabled {
+            policy_options {mode: MANDATORY}
+            AutoFillEnabled: false
+          }
+        }
+      })");
+}  // namespace
+
+ImageFetchOperation::ImageFetchOperation(size_t image_count,
+                                         CardArtImagesFetchedCallback callback)
+    : pending_request_count_(image_count),
+      all_fetches_complete_callback_(std::move(callback)) {}
+
+void ImageFetchOperation::ImageFetched(const std::string& card_server_id,
+                                       const gfx::Image& card_art_image) {
+  pending_request_count_--;
+
+  if (!card_art_image.IsEmpty())
+    fetched_card_art_images_[card_server_id] = card_art_image;
+
+  if (pending_request_count_ == 0U) {
+    std::move(all_fetches_complete_callback_)
+        .Run(std::move(fetched_card_art_images_));
+  }
+}
+
+ImageFetchOperation::~ImageFetchOperation() = default;
+
+AutofillImageFetcher::AutofillImageFetcher(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    std::unique_ptr<image_fetcher::ImageDecoder> image_decoder) {
+  if (url_loader_factory && image_decoder) {
+    image_fetcher_ = std::make_unique<image_fetcher::ImageFetcherImpl>(
+        std::move(image_decoder), std::move(url_loader_factory));
+  }
+}
+
+AutofillImageFetcher::~AutofillImageFetcher() = default;
+
+void AutofillImageFetcher::FetchImagesForUrls(
+    const std::map<std::string, GURL>& card_server_ids_and_art_urls,
+    CardArtImagesFetchedCallback callback) {
+  if (!image_fetcher_) {
+    std::move(callback).Run({});
+    return;
+  }
+
+  auto image_fetcher_operation = base::MakeRefCounted<ImageFetchOperation>(
+      card_server_ids_and_art_urls.size(), std::move(callback));
+
+  for (const auto& card_server_id_and_art_url : card_server_ids_and_art_urls) {
+    FetchImageForUrl(image_fetcher_operation, card_server_id_and_art_url.first,
+                     card_server_id_and_art_url.second);
+  }
+}
+
+void AutofillImageFetcher::FetchImageForUrl(
+    const scoped_refptr<ImageFetchOperation>& operation,
+    const std::string& card_server_id,
+    const GURL& card_art_url) {
+  if (!card_art_url.is_valid()) {
+    OnCardArtImageFetched(operation, card_server_id, gfx::Image(),
+                          image_fetcher::RequestMetadata());
+    return;
+  }
+
+  image_fetcher::ImageFetcherParams params(kCardArtImageTrafficAnnotation,
+                                           kUmaClientName);
+  image_fetcher_->FetchImage(
+      card_art_url,
+      base::BindOnce(&AutofillImageFetcher::OnCardArtImageFetched, operation,
+                     card_server_id),
+      std::move(params));
+}
+
+// static
+void AutofillImageFetcher::OnCardArtImageFetched(
+    const scoped_refptr<ImageFetchOperation>& operation,
+    const std::string& card_server_id,
+    const gfx::Image& card_art_image,
+    const image_fetcher::RequestMetadata& metadata) {
+  operation->ImageFetched(card_server_id, card_art_image);
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/autofill_image_fetcher.h b/components/autofill/core/browser/ui/autofill_image_fetcher.h
new file mode 100644
index 0000000..4f269fb
--- /dev/null
+++ b/components/autofill/core/browser/ui/autofill_image_fetcher.h
@@ -0,0 +1,110 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_AUTOFILL_IMAGE_FETCHER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_AUTOFILL_IMAGE_FETCHER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/scoped_refptr.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace gfx {
+class Image;
+}  // namespace gfx
+
+namespace image_fetcher {
+class ImageDecoder;
+class ImageFetcher;
+struct RequestMetadata;
+}  // namespace image_fetcher
+
+namespace network {
+class SharedURLLoaderFactory;
+}  // namespace network
+
+class GURL;
+
+namespace autofill {
+
+using CardArtImagesFetchedCallback = base::OnceCallback<void(
+    const std::map<std::string, gfx::Image>& card_art_images)>;
+
+// The image fetching operation instance. It tracks the state of the paired
+// request. Will be created when the AutofillImageFetcher receives a request to
+// fetch images for a vector of urls.
+class ImageFetchOperation : public base::RefCounted<ImageFetchOperation> {
+ public:
+  ImageFetchOperation(size_t image_count,
+                      CardArtImagesFetchedCallback callback);
+  ImageFetchOperation(const ImageFetchOperation&) = delete;
+  ImageFetchOperation& operator=(const ImageFetchOperation&) = delete;
+
+  // Invoked when an image fetch is complete and data is returned.
+  void ImageFetched(const std::string& card_server_id,
+                    const gfx::Image& card_art_image);
+
+ private:
+  friend class base::RefCounted<ImageFetchOperation>;
+
+  ~ImageFetchOperation();
+
+  // The number of images that should be fetched before completion.
+  size_t pending_request_count_ = 0;
+
+  // The mapping of each credit card's server id to its card art image.
+  std::map<std::string, gfx::Image> fetched_card_art_images_;
+
+  // Callback function to be invoked when fetching is finished.
+  CardArtImagesFetchedCallback all_fetches_complete_callback_;
+};
+
+// Fetches images stored outside of Chrome for autofill.
+class AutofillImageFetcher : public KeyedService {
+ public:
+  AutofillImageFetcher(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      std::unique_ptr<image_fetcher::ImageDecoder> image_decoder);
+  ~AutofillImageFetcher() override;
+  AutofillImageFetcher(const AutofillImageFetcher&) = delete;
+  AutofillImageFetcher& operator=(const AutofillImageFetcher&) = delete;
+
+  // Once invoked, the |image_fetcher_| will start fetching images based on the
+  // urls. |card_server_ids_and_art_urls| is a map with credit cards' server id
+  // as the key and its card art image url as the value. |callback| will be
+  // invoked when all the requests have been completed. The callback will
+  // receive a map of card server IDs to images, for (only) those cards for
+  // which the AutofillImageFetcher could successfully fetch the image.
+  void FetchImagesForUrls(
+      const std::map<std::string, GURL>& card_server_ids_and_art_urls,
+      CardArtImagesFetchedCallback callback);
+
+ protected:
+  // Helper function to fetch art image for card with |card_server_id| given the
+  // |card_art_url|, for the specific |operation| instance.
+  virtual void FetchImageForUrl(
+      const scoped_refptr<ImageFetchOperation>& operation,
+      const std::string& card_server_id,
+      const GURL& card_art_url);
+
+  // The image fetcher implementation.
+  std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher_;
+
+ private:
+  friend class AutofillImageFetcherTest;
+
+  // Called when an image is fetched for the |operation| instance.
+  static void OnCardArtImageFetched(
+      const scoped_refptr<ImageFetchOperation>& operation,
+      const std::string& card_server_id,
+      const gfx::Image& card_art_image,
+      const image_fetcher::RequestMetadata& metadata);
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_AUTOFILL_IMAGE_FETCHER_H_
diff --git a/components/autofill/core/browser/ui/autofill_image_fetcher_unittest.cc b/components/autofill/core/browser/ui/autofill_image_fetcher_unittest.cc
new file mode 100644
index 0000000..0b33202
--- /dev/null
+++ b/components/autofill/core/browser/ui/autofill_image_fetcher_unittest.cc
@@ -0,0 +1,175 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/ui/autofill_image_fetcher.h"
+
+#include "base/callback_helpers.h"
+#include "base/test/bind.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/image_fetcher/core/image_decoder.h"
+#include "components/image_fetcher/core/mock_image_fetcher.h"
+#include "components/image_fetcher/core/request_metadata.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_unittest_util.h"
+#include "ui/resources/grit/ui_resources.h"
+
+using testing::_;
+
+namespace autofill {
+
+class TestAutofillImageFetcher : public AutofillImageFetcher {
+ public:
+  explicit TestAutofillImageFetcher(
+      std::unique_ptr<image_fetcher::MockImageFetcher> image_fetcher)
+      : AutofillImageFetcher(nullptr, nullptr) {
+    image_fetcher_ = std::move(image_fetcher);
+  }
+
+  ~TestAutofillImageFetcher() override = default;
+
+  void FetchImageForUrl(const scoped_refptr<ImageFetchOperation>& operation,
+                        const std::string& card_server_id,
+                        const GURL& card_art_url) override {
+    AutofillImageFetcher::FetchImageForUrl(operation, card_server_id,
+                                           card_art_url);
+    current_operation_ = operation;
+  }
+
+  const scoped_refptr<ImageFetchOperation>& current_operation() {
+    return current_operation_;
+  }
+
+ private:
+  scoped_refptr<ImageFetchOperation> current_operation_;
+};
+
+class AutofillImageFetcherTest : public testing::Test {
+ public:
+  void SetUp() override {
+    auto image_fetcher = std::make_unique<image_fetcher::MockImageFetcher>();
+    image_fetcher_ = image_fetcher.get();
+    autofill_image_fetcher_ =
+        std::make_unique<TestAutofillImageFetcher>(std::move(image_fetcher));
+  }
+
+  void SimulateOnImageFetched(const std::string& server_id,
+                              const gfx::Image& image) {
+    TestAutofillImageFetcher::OnCardArtImageFetched(
+        autofill_image_fetcher()->current_operation(), server_id, image,
+        image_fetcher::RequestMetadata());
+  }
+
+  void ValidateResult(std::map<std::string, gfx::Image> received_images,
+                      std::map<std::string, gfx::Image> expected_images) {
+    ASSERT_EQ(expected_images.size(), received_images.size());
+    for (const auto& expected_pair : expected_images) {
+      const gfx::Image& expected_image = expected_pair.second;
+      const gfx::Image& received_image = received_images[expected_pair.first];
+      EXPECT_TRUE(gfx::test::AreImagesEqual(expected_image, received_image));
+    }
+  }
+
+  image_fetcher::MockImageFetcher* mock_image_fetcher() {
+    return image_fetcher_;
+  }
+
+  TestAutofillImageFetcher* autofill_image_fetcher() {
+    return autofill_image_fetcher_.get();
+  }
+
+ private:
+  std::unique_ptr<TestAutofillImageFetcher> autofill_image_fetcher_;
+  image_fetcher::MockImageFetcher* image_fetcher_;
+};
+
+TEST_F(AutofillImageFetcherTest, FetchImage_Success) {
+  // The credit card network images cannot be found in the tests, but it should
+  // be okay since we don't care what the images are.
+  gfx::Image fake_image1 =
+      ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
+          IDR_DEFAULT_FAVICON);
+  gfx::Image fake_image2 =
+      ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
+          IDR_DEFAULT_FAVICON_DARK);
+  std::map<std::string, gfx::Image> expected_images = {
+      {"server_id1", fake_image1}, {"server_id2", fake_image2}};
+
+  // Expect callback to be called with some received images.
+  std::map<std::string, gfx::Image> received_images;
+  auto callback = base::BindLambdaForTesting(
+      [&](const std::map<std::string, gfx::Image>& card_art_image_map) {
+        received_images = card_art_image_map;
+      });
+
+  // Expect to be called twice.
+  EXPECT_CALL(*mock_image_fetcher(), FetchImageAndData_(_, _, _, _)).Times(2);
+  std::map<std::string, GURL> url_map = {
+      {"server_id1", GURL("http://www.example.com/fake_image1")},
+      {"server_id2", GURL("http://www.example.com/fake_image2")}};
+  autofill_image_fetcher()->FetchImagesForUrls(url_map, callback);
+
+  // Simulate successful image fetching (for image with URL) -> expect the
+  // callback to be called.
+  SimulateOnImageFetched("server_id1", fake_image1);
+  SimulateOnImageFetched("server_id2", fake_image2);
+
+  ValidateResult(received_images, expected_images);
+}
+
+TEST_F(AutofillImageFetcherTest, FetchImage_InvalidUrlFailure) {
+  gfx::Image fake_image1 =
+      ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
+          IDR_DEFAULT_FAVICON);
+  std::map<std::string, gfx::Image> expected_images = {
+      {"server_id1", fake_image1}};
+
+  // Expect callback to be called with expected images.
+  std::map<std::string, gfx::Image> received_images;
+  auto callback = base::BindLambdaForTesting(
+      [&](const std::map<std::string, gfx::Image>& card_art_image_map) {
+        received_images = card_art_image_map;
+      });
+
+  // Expect to be called once with one invalid url.
+  EXPECT_CALL(*mock_image_fetcher(), FetchImageAndData_(_, _, _, _)).Times(1);
+  std::map<std::string, GURL> url_map = {
+      {"server_id1", GURL("http://www.example.com/fake_image1")},
+      {"server_id2", GURL("")}};
+  autofill_image_fetcher()->FetchImagesForUrls(url_map, callback);
+
+  // Simulate successful image fetching (for image with URL) -> expect the
+  // callback to be called.
+  SimulateOnImageFetched("server_id1", fake_image1);
+
+  ValidateResult(received_images, expected_images);
+}
+
+TEST_F(AutofillImageFetcherTest, FetchImage_ServerFailure) {
+  std::map<std::string, gfx::Image> expected_images = {};
+
+  // Expect callback to be called with some received images.
+  std::map<std::string, gfx::Image> received_images;
+  auto callback = base::BindLambdaForTesting(
+      [&](const std::map<std::string, gfx::Image>& card_art_image_map) {
+        received_images = card_art_image_map;
+      });
+
+  // Expect to be called once.
+  EXPECT_CALL(*mock_image_fetcher(), FetchImageAndData_(_, _, _, _)).Times(1);
+  std::map<std::string, GURL> url_map = {
+      {"server_id1", GURL("http://www.example.com/fake_image1")}};
+  autofill_image_fetcher()->FetchImagesForUrls(url_map, callback);
+
+  // Simulate failed image fetching (for image with URL) -> expect the
+  // callback to be called.
+  SimulateOnImageFetched("server_id1", gfx::Image());
+
+  ValidateResult(received_images, expected_images);
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/common/signatures.h b/components/autofill/core/common/signatures.h
index 21257dc..607f37b 100644
--- a/components/autofill/core/common/signatures.h
+++ b/components/autofill/core/common/signatures.h
@@ -9,7 +9,7 @@
 #include <stdint.h>
 
 #include "base/strings/string_piece.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 
 namespace autofill {
 
@@ -17,8 +17,8 @@
 struct FormFieldData;
 
 namespace internal {
-using FormSignatureType = ::util::IdTypeU64<class FormSignatureMarker>;
-using FieldSignatureType = ::util::IdTypeU32<class FieldSignatureMarker>;
+using FormSignatureType = ::base::IdTypeU64<class FormSignatureMarker>;
+using FieldSignatureType = ::base::IdTypeU32<class FieldSignatureMarker>;
 }  // namespace internal
 
 // The below strong aliases are defined as subclasses instead of typedefs in
diff --git a/components/autofill/core/common/unique_ids.h b/components/autofill/core/common/unique_ids.h
index b3fc70c..2bf4c579 100644
--- a/components/autofill/core/common/unique_ids.h
+++ b/components/autofill/core/common/unique_ids.h
@@ -9,15 +9,15 @@
 #include <limits>
 #include <ostream>
 
+#include "base/types/id_type.h"
 #include "base/unguessable_token.h"
-#include "base/util/type_safety/id_type.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 
 namespace autofill {
 
 namespace internal {
 
-// TokenType wraps an base::UnguessableToken just like util::TokenType but
+// TokenType wraps an base::UnguessableToken just like base::TokenType but
 // initializes to zero by default. We use it to define our own versions of
 // LocalFrameToken and RemoteFrameToken to avoid dependencies on blink here and
 // in the mojo code, since iOS depends on this code.
@@ -54,8 +54,8 @@
 
 namespace internal {
 
-using FormRendererIdType = ::util::IdTypeU32<class FormRendererIdMarker>;
-using FieldRendererIdType = ::util::IdTypeU32<class FieldRendererIdMarker>;
+using FormRendererIdType = ::base::IdTypeU32<class FormRendererIdMarker>;
+using FieldRendererIdType = ::base::IdTypeU32<class FieldRendererIdMarker>;
 
 }  // namespace internal
 
diff --git a/components/browser_ui/styles/android/java/res/values-night/styles.xml b/components/browser_ui/styles/android/java/res/values-night/styles.xml
index c08ed325..cd3d8a6 100644
--- a/components/browser_ui/styles/android/java/res/values-night/styles.xml
+++ b/components/browser_ui/styles/android/java/res/values-night/styles.xml
@@ -17,6 +17,7 @@
         <item name="colorSurface">@color/modern_grey_900</item>
         <item name="colorOnBackground">@color/modern_white</item>
         <item name="colorOnPrimary">@color/modern_blue_800</item>
+        <item name="colorOnPrimaryContainer">@color/baseline_primary_900</item>
         <item name="colorOnSurface">@color/modern_grey_100</item>
         <item name="colorOnSurfaceVariant">@color/white_alpha_70</item>
         <item name="colorOnSurfaceInverse">@color/modern_grey_800</item>
diff --git a/components/browser_ui/styles/android/java/res/values/styles.xml b/components/browser_ui/styles/android/java/res/values/styles.xml
index 7b6d19f..9000b14 100644
--- a/components/browser_ui/styles/android/java/res/values/styles.xml
+++ b/components/browser_ui/styles/android/java/res/values/styles.xml
@@ -202,6 +202,7 @@
         <item name="colorSurfaceVariant">@color/baseline_neutral_variant_100</item>
         <item name="colorOnBackground">@color/modern_grey_900</item>
         <item name="colorOnPrimary">@color/modern_white</item>
+        <item name="colorOnPrimaryContainer">@color/baseline_primary_900</item>
         <item name="colorOnSurface">@color/modern_grey_900</item>
         <item name="colorOnSurfaceVariant">@color/modern_grey_700</item>
         <item name="colorOnSurfaceInverse">@color/modern_white</item>
diff --git a/components/client_hints/README.md b/components/client_hints/README.md
index 3c224d75..02d2615e 100644
--- a/components/client_hints/README.md
+++ b/components/client_hints/README.md
@@ -64,7 +64,7 @@
 
 ### Client Hints Infrastructure
 
-The client hints set is passed into the document on commit from [NavigationRequest::CommitNavigation](/content/browser/renderer_host/navigation_request.cc) to the document and is used in [FrameFetchContext::AddClientHintsIfNecessary](third_party/blink/renderer/core/loader/frame_fetch_context.cc), where all of the relevant client hint information filled into the headers to be sent.
+The client hints set is passed into the document on commit from [NavigationRequest::CommitNavigation](/content/browser/renderer_host/navigation_request.cc) to the document and is used in [FrameFetchContext::AddClientHintsIfNecessary](/third_party/blink/renderer/core/loader/frame_fetch_context.cc), where all of the relevant client hint information filled into the headers to be sent.
 
 ### Critical-CH response header
 
diff --git a/components/feed/core/v2/feed_network_impl.cc b/components/feed/core/v2/feed_network_impl.cc
index ebbb4f61..5d1593f 100644
--- a/components/feed/core/v2/feed_network_impl.cc
+++ b/components/feed/core/v2/feed_network_impl.cc
@@ -96,8 +96,8 @@
       request_type, raw_response.response_info.status_code,
       raw_response.response_info.fetch_duration);
   FeedNetwork::QueryRequestResult result;
-  result.response_info.fetch_time_ticks = base::TimeTicks::Now();
   result.response_info = raw_response.response_info;
+  result.response_info.fetch_time_ticks = base::TimeTicks::Now();
   if (result.response_info.status_code == 200) {
     ::google::protobuf::io::CodedInputStream input_stream(
         reinterpret_cast<const uint8_t*>(raw_response.response_bytes.data()),
diff --git a/components/feed/core/v2/public/types.h b/components/feed/core/v2/public/types.h
index 25c2454..1e86955 100644
--- a/components/feed/core/v2/public/types.h
+++ b/components/feed/core/v2/public/types.h
@@ -11,7 +11,7 @@
 
 #include "base/strings/string_piece.h"
 #include "base/time/time.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "base/version.h"
 #include "components/version_info/channel.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -39,9 +39,9 @@
 };
 
 // A unique ID for an ephemeral change.
-using EphemeralChangeId = util::IdTypeU32<class EphemeralChangeIdClass>;
-using SurfaceId = util::IdTypeU32<class SurfaceIdClass>;
-using ImageFetchId = util::IdTypeU32<class ImageFetchIdClass>;
+using EphemeralChangeId = base::IdTypeU32<class EphemeralChangeIdClass>;
+using SurfaceId = base::IdTypeU32<class SurfaceIdClass>;
+using ImageFetchId = base::IdTypeU32<class ImageFetchIdClass>;
 
 // A map of trial names (key) to group names (value) that is
 // sent from the server.
@@ -171,7 +171,7 @@
 std::ostream& operator<<(std::ostream& out,
                          WebFeedSubscriptionRequestStatus value);
 
-using NetworkRequestId = util::IdTypeU32<class NetworkRequestIdClass>;
+using NetworkRequestId = base::IdTypeU32<class NetworkRequestIdClass>;
 
 }  // namespace feed
 
diff --git a/components/feed/core/v2/stream_model/feature_tree.h b/components/feed/core/v2/stream_model/feature_tree.h
index 90cde01a..241c0856 100644
--- a/components/feed/core/v2/stream_model/feature_tree.h
+++ b/components/feed/core/v2/stream_model/feature_tree.h
@@ -10,7 +10,7 @@
 #include <utility>
 #include <vector>
 
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "components/feed/core/proto/v2/store.pb.h"
 #include "components/feed/core/v2/proto_util.h"
 #include "components/feed/core/v2/types.h"
@@ -19,7 +19,7 @@
 namespace stream_model {
 
 // Uniquely identifies a feedwire::ContentId. Provided by |ContentMap|.
-using ContentTag = util::IdTypeU32<class ContentTagClass>;
+using ContentTag = base::IdTypeU32<class ContentTagClass>;
 using ContentRevision = feed::ContentRevision;
 
 // Owns instances of feedstore::Content pointed to by the feature tree, and
diff --git a/components/feed/core/v2/types.h b/components/feed/core/v2/types.h
index fc2c66d..338416e 100644
--- a/components/feed/core/v2/types.h
+++ b/components/feed/core/v2/types.h
@@ -10,7 +10,7 @@
 
 #include "base/containers/flat_set.h"
 #include "base/time/time.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "base/values.h"
 #include "components/feed/core/proto/v2/wire/reliability_logging_enums.pb.h"
 #include "components/feed/core/v2/enums.h"
@@ -28,10 +28,10 @@
 
 // Uniquely identifies a revision of a |feedstore::Content|. If Content changes,
 // it is assigned a new revision number.
-using ContentRevision = util::IdTypeU32<class ContentRevisionClass>;
+using ContentRevision = base::IdTypeU32<class ContentRevisionClass>;
 
 // ID for a stored pending action.
-using LocalActionId = util::IdType32<class LocalActionIdClass>;
+using LocalActionId = base::IdType32<class LocalActionIdClass>;
 
 std::string ToString(ContentRevision c);
 ContentRevision ToContentRevision(const std::string& str);
diff --git a/components/optimization_guide/core/optimization_guide_enums.h b/components/optimization_guide/core/optimization_guide_enums.h
index a48c6ca..639ffb31 100644
--- a/components/optimization_guide/core/optimization_guide_enums.h
+++ b/components/optimization_guide/core/optimization_guide_enums.h
@@ -150,11 +150,14 @@
   kFailedModelInfoInvalid = 9,
   // The CRX file was a valid CRX file but did not come from a valid publisher.
   kFailedCrxInvalidPublisher = 10,
-  // The model directory for storing model files does not exist.
-  kModelDirectoryDoesNotExist = 11,
+  // The opt guide parent directory for storing models in does not exist.
+  kOptGuideDirectoryDoesNotExist = 11,
+  // The new directory to persist this model version's files could not be
+  // created.
+  kCouldNotCreateDirectory = 12,
 
   // Add new values above this line.
-  kMaxValue = kModelDirectoryDoesNotExist,
+  kMaxValue = kCouldNotCreateDirectory,
 };
 
 // The state of the model file needed for execution.
diff --git a/components/optimization_guide/core/optimization_guide_store.cc b/components/optimization_guide/core/optimization_guide_store.cc
index 07e617c9..6bf1980 100644
--- a/components/optimization_guide/core/optimization_guide_store.cc
+++ b/components/optimization_guide/core/optimization_guide_store.cc
@@ -917,6 +917,11 @@
                      std::move(callback)));
 }
 
+// This method is called during browser startup when we need to check if any
+// models have expired. When this occurs, |update_vector| and |remove_vector|
+// will both be empty. Otherwise, this method may also be called whenever a
+// model is updated or its deletion is requested in which case one of or both
+// |update_vector| and |remove_vector| will have entries.
 void OptimizationGuideStore::OnLoadModelsToBeUpdated(
     std::unique_ptr<EntryVector> update_vector,
     std::unique_ptr<leveldb_proto::KeyVector> remove_vector,
@@ -934,8 +939,8 @@
       !update_vector->empty() || !remove_vector->empty();
   for (const auto& entry : *entries) {
     bool should_delete_download_file = had_entries_to_update_or_remove;
-    // Only look to purge if we weren't explicitly passed in entries to update
-    // or remove.
+    // Only check expiry if we weren't explicitly passed in entries to update or
+    // remove.
     if (!had_entries_to_update_or_remove) {
       if (entry.second.has_expiry_time_secs()) {
         if (entry.second.expiry_time_secs() <= now_since_epoch) {
@@ -945,8 +950,8 @@
                                     true);
         }
       } else {
-        // If we were purging and the entry did not have an expiration time
-        // associated with it, add one.
+        // If we were checking expiry and the entry did not have an expiration
+        // time associated with it, add one with a default TTL.
         update_vector->push_back(entry);
         update_vector->back().second.set_expiry_time_secs(
             now_since_epoch +
@@ -954,14 +959,41 @@
       }
     }
 
-    // Delete models that are provided via file.
+    // Delete files (the model itself and any additional files) that are
+    // provided by the model in its directory.
     if (should_delete_download_file && entry.second.has_prediction_model() &&
         entry.second.prediction_model().model().has_download_url()) {
+      // |GetFilePathFromPredictionModel| never returns nullopt when
+      // |model().has_download_url()| is true.
+      base::FilePath model_file_path =
+          GetFilePathFromPredictionModel(entry.second.prediction_model())
+              .value();
+      base::FilePath path_to_delete;
+
+      // Backwards compatibility: Once upon a time (<M93), model files were
+      // stored as
+      // `$CHROME_DATA/OptGuideModels/${MODELTARGET}_${MODELVERSION}.tfl` but
+      // were later moved to
+      // `$CHROME_DATA/OptGuideModels/${MODELTARGET}_${MODELVERSION}/model.tfl`
+      // to support additional files to be packaged alongside the model. Since
+      // the current code needs to recursively delete the whole directory, we'd
+      // normally just take the directory name of the model file. However, doing
+      // this on a freshly updated browser to newer code would cause the entire
+      // OptGuide directory to be blown away, causing collateral damage to other
+      // downloaded models. This is detected by checking whether the base name
+      // of the model file is the old or new version, and acting accordingly.
+      if (model_file_path.BaseName() == GetBaseFileNameForModels()) {
+        path_to_delete = model_file_path.DirName();
+      } else {
+        path_to_delete = model_file_path;
+      }
+
+      // Note that the delete function doesn't care whether the target is a
+      // directory or file. But in the case of a directory, it is recursively
+      // deleted.
       store_task_runner_->PostTask(
-          FROM_HERE, base::BindOnce(base::GetDeleteFileCallback(),
-                                    GetFilePathFromPredictionModel(
-                                        entry.second.prediction_model())
-                                        .value()));
+          FROM_HERE, base::BindOnce(base::GetDeletePathRecursivelyCallback(),
+                                    path_to_delete));
     }
   }
 
diff --git a/components/optimization_guide/core/optimization_guide_store_unittest.cc b/components/optimization_guide/core/optimization_guide_store_unittest.cc
index 4d6669cf..4ecc060 100644
--- a/components/optimization_guide/core/optimization_guide_store_unittest.cc
+++ b/components/optimization_guide/core/optimization_guide_store_unittest.cc
@@ -2190,13 +2190,18 @@
   CreateDatabase();
   InitializeStore(schema_state);
 
+  base::FilePath old_dir = temp_dir().AppendASCII("model_v1");
+  base::FilePath new_dir = temp_dir().AppendASCII("model_v2");
+  ASSERT_TRUE(base::CreateDirectory(old_dir));
+  ASSERT_TRUE(base::CreateDirectory(new_dir));
+
   base::Time update_time = base::Time().Now();
   std::unique_ptr<StoreUpdateData> update_data =
       guide_store()->CreateUpdateDataForPredictionModels(
           update_time +
           optimization_guide::features::StoredModelsInactiveDuration());
   ASSERT_TRUE(update_data);
-  base::FilePath old_file_path = temp_dir().AppendASCII("oldfile");
+  base::FilePath old_file_path = old_dir.AppendASCII("model.tflite");
   ASSERT_EQ(static_cast<int32_t>(3), base::WriteFile(old_file_path, "boo", 3));
   SeedPredictionModelUpdateData(update_data.get(),
                                 proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
@@ -2213,7 +2218,7 @@
           update_time +
           optimization_guide::features::StoredModelsInactiveDuration());
   ASSERT_TRUE(update_data2);
-  base::FilePath new_file_path = temp_dir().AppendASCII("newfile");
+  base::FilePath new_file_path = new_dir.AppendASCII("model.tflite");
   ASSERT_EQ(static_cast<int32_t>(3), base::WriteFile(new_file_path, "boo", 3));
   SeedPredictionModelUpdateData(update_data2.get(),
                                 proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
@@ -2223,6 +2228,70 @@
 
   // Make sure old file is deleted when model updated for target.
   EXPECT_FALSE(base::PathExists(old_file_path));
+  EXPECT_FALSE(base::PathExists(old_dir));
+
+  EXPECT_TRUE(base::PathExists(new_file_path));
+  EXPECT_TRUE(base::PathExists(new_dir));
+}
+
+TEST_F(OptimizationGuideStoreTest,
+       BackwardCompatibilityForParentDirectoryStorage) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  SeedInitialData(schema_state, 0);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  base::FilePath old_dir = temp_dir().AppendASCII("OptGuideModels");
+  base::FilePath new_dir = old_dir.AppendASCII("foo_target_v2");
+  ASSERT_TRUE(base::CreateDirectory(old_dir));
+  ASSERT_TRUE(base::CreateDirectory(new_dir));
+
+  base::FilePath old_unassociated_model_path =
+      old_dir.AppendASCII("other_model.tflite");
+  ASSERT_EQ(static_cast<int32_t>(3),
+            base::WriteFile(old_unassociated_model_path, "boo", 3));
+
+  base::Time update_time = base::Time().Now();
+  std::unique_ptr<StoreUpdateData> update_data =
+      guide_store()->CreateUpdateDataForPredictionModels(
+          update_time +
+          optimization_guide::features::StoredModelsInactiveDuration());
+  ASSERT_TRUE(update_data);
+  base::FilePath old_file_path = old_dir.AppendASCII("model_v1.tflite");
+  ASSERT_EQ(static_cast<int32_t>(3), base::WriteFile(old_file_path, "boo", 3));
+  SeedPredictionModelUpdateData(update_data.get(),
+                                proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
+                                old_file_path);
+  UpdatePredictionModels(std::move(update_data));
+
+  OptimizationGuideStore::EntryKey entry_key;
+  bool success = guide_store()->FindPredictionModelEntryKey(
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
+  EXPECT_TRUE(success);
+
+  std::unique_ptr<StoreUpdateData> update_data2 =
+      guide_store()->CreateUpdateDataForPredictionModels(
+          update_time +
+          optimization_guide::features::StoredModelsInactiveDuration());
+  ASSERT_TRUE(update_data2);
+  base::FilePath new_file_path = new_dir.Append(GetBaseFileNameForModels());
+  ASSERT_EQ(static_cast<int32_t>(3), base::WriteFile(new_file_path, "boo", 3));
+  SeedPredictionModelUpdateData(update_data2.get(),
+                                proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
+                                new_file_path);
+  UpdatePredictionModels(std::move(update_data2));
+  RunUntilIdle();
+
+  // The old file should have been deleted, but not its containing directory
+  // because that is recognized as the OptGuideModel parent storage directory.
+  EXPECT_FALSE(base::PathExists(old_file_path));
+  EXPECT_TRUE(base::PathExists(old_dir));
+
+  // The other unassociated old-code model should not have been touched.
+  EXPECT_TRUE(base::PathExists(old_unassociated_model_path));
+
+  EXPECT_TRUE(base::PathExists(new_file_path));
+  EXPECT_TRUE(base::PathExists(new_dir));
 }
 
 TEST_F(OptimizationGuideStoreTest, RemovePredictionModelEntryKeyDeletesFile) {
diff --git a/components/optimization_guide/core/optimization_guide_util.cc b/components/optimization_guide/core/optimization_guide_util.cc
index 11e2ff1..059aa4ed 100644
--- a/components/optimization_guide/core/optimization_guide_util.cc
+++ b/components/optimization_guide/core/optimization_guide_util.cc
@@ -107,4 +107,8 @@
 #endif
 }
 
+base::FilePath GetBaseFileNameForModels() {
+  return base::FilePath(FILE_PATH_LITERAL("model.tflite"));
+}
+
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/optimization_guide_util.h b/components/optimization_guide/core/optimization_guide_util.h
index e27f6ba2..7a62a00b 100644
--- a/components/optimization_guide/core/optimization_guide_util.h
+++ b/components/optimization_guide/core/optimization_guide_util.h
@@ -41,6 +41,9 @@
 void SetFilePathInPredictionModel(const base::FilePath& file_path,
                                   proto::PredictionModel* model);
 
+// Returns the base file name to use for storing all prediction models.
+base::FilePath GetBaseFileNameForModels();
+
 // Validates that the metadata stored in |any_metadata_| is of the same type
 // and is parseable as |T|. Will return metadata if all checks pass.
 template <class T,
diff --git a/components/page_info/page_info.h b/components/page_info/page_info.h
index 983f475..ea8d1b2 100644
--- a/components/page_info/page_info.h
+++ b/components/page_info/page_info.h
@@ -144,6 +144,8 @@
     PAGE_INFO_FORGET_SITE_OPENED = 17,
     PAGE_INFO_FORGET_SITE_CLEARED = 18,
     PAGE_INFO_HISTORY_OPENED = 19,
+    PAGE_INFO_HISTORY_ENTRY_REMOVED = 20,
+    PAGE_INFO_HISTORY_ENTRY_CLICKED = 21,
     PAGE_INFO_COUNT
   };
 
diff --git a/components/password_manager/core/browser/leak_detection/BUILD.gn b/components/password_manager/core/browser/leak_detection/BUILD.gn
index c72439b..af1d7ab 100644
--- a/components/password_manager/core/browser/leak_detection/BUILD.gn
+++ b/components/password_manager/core/browser/leak_detection/BUILD.gn
@@ -16,7 +16,6 @@
   ]
   deps = [
     "//base",
-    "//base/util/type_safety",
     "//url",
   ]
 }
diff --git a/components/pdf/renderer/BUILD.gn b/components/pdf/renderer/BUILD.gn
index 2c45dde..4b05f22eb 100644
--- a/components/pdf/renderer/BUILD.gn
+++ b/components/pdf/renderer/BUILD.gn
@@ -2,21 +2,27 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/features.gni")
 import("//pdf/features.gni")
 
 assert(enable_pdf)
 
 static_library("renderer") {
-  sources = [
-    "pdf_accessibility_tree.cc",
+  friend = [ ":unit_tests" ]
+
+  public = [
+    "internal_plugin_renderer_helpers.h",
     "pdf_accessibility_tree.h",
-    "pdf_ax_action_target.cc",
     "pdf_ax_action_target.h",
-    "pepper_pdf_host.cc",
     "pepper_pdf_host.h",
   ]
 
+  sources = [
+    "internal_plugin_renderer_helpers.cc",
+    "pdf_accessibility_tree.cc",
+    "pdf_ax_action_target.cc",
+    "pepper_pdf_host.cc",
+  ]
+
   deps = [
     "//base",
     "//components/resources:components_resources",
@@ -27,6 +33,7 @@
     "//ipc",
     "//pdf:buildflags",
     "//pdf:features",
+    "//pdf:pdf_view_web_plugin",
     "//ppapi/host",
     "//ppapi/proxy",
     "//ppapi/proxy:ipc",
@@ -34,6 +41,7 @@
     "//third_party/blink/public:blink",
     "//third_party/blink/public/strings:strings_grit",
     "//third_party/icu",
+    "//url",
     "//v8",
   ]
 
diff --git a/components/pdf/renderer/DEPS b/components/pdf/renderer/DEPS
index 9a0f74c..a9ba6f8 100644
--- a/components/pdf/renderer/DEPS
+++ b/components/pdf/renderer/DEPS
@@ -6,6 +6,7 @@
   "+pdf/buildflags.h",
   "+pdf/mojom/pdf.mojom.h",
   "+pdf/pdf_features.h",
+  "+pdf/pdf_view_web_plugin.h",
   "+ppapi",
   "+third_party/blink/public",
   "+ui/accessibility",
diff --git a/components/pdf/renderer/internal_plugin_renderer_helpers.cc b/components/pdf/renderer/internal_plugin_renderer_helpers.cc
new file mode 100644
index 0000000..75d5cee6
--- /dev/null
+++ b/components/pdf/renderer/internal_plugin_renderer_helpers.cc
@@ -0,0 +1,57 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/pdf/renderer/internal_plugin_renderer_helpers.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/feature_list.h"
+#include "content/public/renderer/render_frame.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "pdf/buildflags.h"
+#include "pdf/mojom/pdf.mojom.h"
+#include "pdf/pdf_features.h"
+#include "pdf/pdf_view_web_plugin.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/web/web_plugin.h"
+#include "third_party/blink/public/web/web_plugin_params.h"
+#include "url/gurl.h"
+
+namespace pdf {
+
+blink::WebPlugin* MaybeCreateInternalPlugin(
+    content::RenderFrame* render_frame,
+    std::unique_ptr<chrome_pdf::PdfViewWebPlugin::PrintClient> print_client,
+    blink::WebPluginParams& params) {
+  // For a PDF plugin, `params.url` holds the plugin's stream URL. If `params`
+  // contains an 'original-url' attribute, reset `params.url` with its original
+  // URL value so that it can be used to determine the plugin's origin.
+  for (size_t i = 0; i < params.attribute_names.size(); ++i) {
+    if (params.attribute_names[i] == "original-url") {
+      params.url = GURL(params.attribute_values[i].Utf16());
+      break;
+    }
+  }
+
+  if (!base::FeatureList::IsEnabled(chrome_pdf::features::kPdfUnseasoned)) {
+    // Let the caller handle Pepper plugin creation.
+    return nullptr;
+  }
+
+#if BUILDFLAG(ENABLE_PDF_UNSEASONED)
+  // Create unseasoned PDF plugin directly, for development purposes.
+  // TODO(crbug.com/1123621): Implement a more permanent solution once the new
+  // PDF viewer process model is approved and in place.
+  mojo::AssociatedRemote<pdf::mojom::PdfService> pdf_service_remote;
+  render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
+      pdf_service_remote.BindNewEndpointAndPassReceiver());
+  return new chrome_pdf::PdfViewWebPlugin(std::move(pdf_service_remote),
+                                          std::move(print_client), params);
+#else   // !BUILDFLAG(ENABLE_PDF_UNSEASONED)
+  return nullptr;
+#endif  // BUILDFLAG(ENABLE_PDF_UNSEASONED)
+}
+
+}  // namespace pdf
diff --git a/components/pdf/renderer/internal_plugin_renderer_helpers.h b/components/pdf/renderer/internal_plugin_renderer_helpers.h
new file mode 100644
index 0000000..93cedf3
--- /dev/null
+++ b/components/pdf/renderer/internal_plugin_renderer_helpers.h
@@ -0,0 +1,37 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PDF_RENDERER_INTERNAL_PLUGIN_RENDERER_HELPERS_H_
+#define COMPONENTS_PDF_RENDERER_INTERNAL_PLUGIN_RENDERER_HELPERS_H_
+
+#include <memory>
+
+#include "pdf/pdf_view_web_plugin.h"
+
+namespace blink {
+class WebPlugin;
+struct WebPluginParams;
+}  // namespace blink
+
+namespace content {
+class RenderFrame;
+}  // namespace content
+
+namespace pdf {
+
+// Tries to create an instance of the internal PDF plugin, returning `nullptr`
+// if the caller should create a Pepper plugin instance instead.
+//
+// `print_client` is optional, and may be `nullptr`.
+//
+// Note that `blink::WebPlugin` has a special life cycle, so it's returned as a
+// raw pointer here.
+blink::WebPlugin* MaybeCreateInternalPlugin(
+    content::RenderFrame* render_frame,
+    std::unique_ptr<chrome_pdf::PdfViewWebPlugin::PrintClient> print_client,
+    blink::WebPluginParams& params);
+
+}  // namespace pdf
+
+#endif  // COMPONENTS_PDF_RENDERER_INTERNAL_PLUGIN_RENDERER_HELPERS_H_
diff --git a/components/performance_manager/public/graph/worker_node.h b/components/performance_manager/public/graph/worker_node.h
index f15d54e..23a275e 100644
--- a/components/performance_manager/public/graph/worker_node.h
+++ b/components/performance_manager/public/graph/worker_node.h
@@ -10,7 +10,7 @@
 #include "base/callback_forward.h"
 #include "base/containers/flat_set.h"
 #include "base/macros.h"
-#include "base/util/type_safety/token_type.h"
+#include "base/types/token_type.h"
 #include "components/performance_manager/public/execution_context_priority/execution_context_priority.h"
 #include "components/performance_manager/public/graph/node.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
diff --git a/components/performance_manager/public/render_process_host_id.h b/components/performance_manager/public/render_process_host_id.h
index 4515293..d4a06dd 100644
--- a/components/performance_manager/public/render_process_host_id.h
+++ b/components/performance_manager/public/render_process_host_id.h
@@ -5,13 +5,13 @@
 #ifndef COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_RENDER_PROCESS_HOST_ID_H_
 #define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_RENDER_PROCESS_HOST_ID_H_
 
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "content/public/common/child_process_host.h"
 
 namespace performance_manager {
 
 using RenderProcessHostIdBase =
-    util::IdType<class RenderProcessHostIdTag,
+    base::IdType<class RenderProcessHostIdTag,
                  int32_t,
                  content::ChildProcessHost::kInvalidUniqueID,
                  1>;
diff --git a/components/performance_manager/public/voting/voting.h b/components/performance_manager/public/voting/voting.h
index 6d0a9ab..e373a39 100644
--- a/components/performance_manager/public/voting/voting.h
+++ b/components/performance_manager/public/voting/voting.h
@@ -44,8 +44,8 @@
 #include "base/check_op.h"
 #include "base/containers/flat_map.h"
 #include "base/dcheck_is_on.h"
+#include "base/types/id_type.h"
 #include "base/types/pass_key.h"
-#include "base/util/type_safety/id_type.h"
 
 namespace performance_manager {
 namespace voting {
@@ -83,7 +83,7 @@
 
 // Identifies a VotingChannel.
 template <typename VoteImpl>
-using VoterId = util::IdTypeU32<VoteImpl>;
+using VoterId = base::IdTypeU32<VoteImpl>;
 
 template <class VoteImpl>
 class VoteObserver {
diff --git a/components/permissions/permission_request_id.h b/components/permissions/permission_request_id.h
index 07836f0..ecdb421 100644
--- a/components/permissions/permission_request_id.h
+++ b/components/permissions/permission_request_id.h
@@ -7,7 +7,7 @@
 
 #include <string>
 
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -24,7 +24,7 @@
 class PermissionRequestID {
  public:
   // Uniquely identifies a request (at least) within a given frame.
-  using RequestLocalId = util::IdType64<PermissionRequestID>;
+  using RequestLocalId = base::IdType64<PermissionRequestID>;
 
   PermissionRequestID(content::RenderFrameHost* render_frame_host,
                       RequestLocalId request_local_id);
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 3f029541..a4e82ff 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -16217,11 +16217,12 @@
       'id': 342,
       'caption': '''Enable component updates in <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph>''',
       'tags': [],
-      'desc': '''Enables component updates for all components in <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> when not set or set to True.
+      'desc': '''Enables component updates for all components in <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> when not set or set to enabled.
 
-      If set to False, updates to components are disabled. However, some components are exempt from this policy: updates to any component that does not contain executable code, or does not significantly alter the behavior of the browser, or is critical for its security will not be disabled.
+      If set to disabled, updates to components are disabled. However, some components are exempt from this policy: updates to any component that does not contain executable code, or does not significantly alter the behavior of the browser, or is critical for its security will not be disabled.
       Examples of such components include the certificate revocation lists and Safe Browsing data.
-      See https://developers.google.com/safe-browsing for more info on Safe Browsing.''',
+      See https://developers.google.com/safe-browsing for more info on Safe Browsing.
+      Please note that setting this policy to disabled can potentially prevent the <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> developers from providing critical security fixes in a timely manner and is thus not recommended.''',
     },
     {
       'name': 'NativePrinters',
diff --git a/components/safe_browsing/android/safe_browsing_api_handler_unittest.cc b/components/safe_browsing/android/safe_browsing_api_handler_unittest.cc
index 95ea634..772053c 100644
--- a/components/safe_browsing/android/safe_browsing_api_handler_unittest.cc
+++ b/components/safe_browsing/android/safe_browsing_api_handler_unittest.cc
@@ -82,63 +82,6 @@
   EXPECT_EQ(empty_meta_, meta_);
 }
 
-TEST_F(SafeBrowsingApiHandlerUtilTest, PhaSubType) {
-  ThreatMetadata expected;
-
-  EXPECT_EQ(UMA_STATUS_MATCH,
-            ResetAndParseJson("{\"matches\":[{\"threat_type\":\"4\", "
-                              "\"pha_pattern_type\":\"LANDING\"}]}"));
-  EXPECT_EQ(SB_THREAT_TYPE_URL_MALWARE, threat_);
-  expected.threat_pattern_type = ThreatPatternType::MALWARE_LANDING;
-  EXPECT_EQ(expected, meta_);
-  // Test the ThreatMetadata comparitor for this field.
-  EXPECT_NE(empty_meta_, meta_);
-
-  EXPECT_EQ(UMA_STATUS_MATCH,
-            ResetAndParseJson("{\"matches\":[{\"threat_type\":\"4\", "
-                              "\"pha_pattern_type\":\"DISTRIBUTION\"}]}"));
-  EXPECT_EQ(SB_THREAT_TYPE_URL_MALWARE, threat_);
-  expected.threat_pattern_type = ThreatPatternType::MALWARE_DISTRIBUTION;
-  EXPECT_EQ(expected, meta_);
-
-  EXPECT_EQ(UMA_STATUS_MATCH,
-            ResetAndParseJson("{\"matches\":[{\"threat_type\":\"4\", "
-                              "\"pha_pattern_type\":\"junk\"}]}"));
-  EXPECT_EQ(empty_meta_, meta_);
-}
-
-TEST_F(SafeBrowsingApiHandlerUtilTest, SocialEngineeringSubType) {
-  ThreatMetadata expected;
-
-  EXPECT_EQ(
-      UMA_STATUS_MATCH,
-      ResetAndParseJson("{\"matches\":[{\"threat_type\":\"5\", "
-                        "\"se_pattern_type\":\"SOCIAL_ENGINEERING_ADS\"}]}"));
-  EXPECT_EQ(SB_THREAT_TYPE_URL_PHISHING, threat_);
-  expected.threat_pattern_type = ThreatPatternType::SOCIAL_ENGINEERING_ADS;
-  EXPECT_EQ(expected, meta_);
-
-  EXPECT_EQ(UMA_STATUS_MATCH,
-            ResetAndParseJson(
-                "{\"matches\":[{\"threat_type\":\"5\", "
-                "\"se_pattern_type\":\"SOCIAL_ENGINEERING_LANDING\"}]}"));
-  EXPECT_EQ(SB_THREAT_TYPE_URL_PHISHING, threat_);
-  expected.threat_pattern_type = ThreatPatternType::SOCIAL_ENGINEERING_LANDING;
-  EXPECT_EQ(expected, meta_);
-
-  EXPECT_EQ(UMA_STATUS_MATCH,
-            ResetAndParseJson("{\"matches\":[{\"threat_type\":\"5\", "
-                              "\"se_pattern_type\":\"PHISHING\"}]}"));
-  EXPECT_EQ(SB_THREAT_TYPE_URL_PHISHING, threat_);
-  expected.threat_pattern_type = ThreatPatternType::PHISHING;
-  EXPECT_EQ(expected, meta_);
-
-  EXPECT_EQ(UMA_STATUS_MATCH,
-            ResetAndParseJson("{\"matches\":[{\"threat_type\":\"5\", "
-                              "\"se_pattern_type\":\"junk\"}]}"));
-  EXPECT_EQ(empty_meta_, meta_);
-}
-
 TEST_F(SafeBrowsingApiHandlerUtilTest, PopulationId) {
   ThreatMetadata expected;
 
@@ -152,23 +95,6 @@
   EXPECT_NE(empty_meta_, meta_);
 }
 
-TEST_F(SafeBrowsingApiHandlerUtilTest, NoSubresourceFilterSubTypes) {
-  ThreatMetadata expected;
-
-  EXPECT_EQ(UMA_STATUS_MATCH,
-            ResetAndParseJson("{\"matches\":[{\"threat_type\":\"13\"}]}"));
-  EXPECT_EQ(SB_THREAT_TYPE_SUBRESOURCE_FILTER, threat_);
-  expected.threat_pattern_type = ThreatPatternType::NONE;
-  EXPECT_EQ(expected, meta_);
-
-  EXPECT_EQ(UMA_STATUS_MATCH,
-            ResetAndParseJson("{\"matches\":[{\"threat_type\":\"13\", "
-                              "\"se_pattern_type\":\"junk\"}]}"));
-  EXPECT_EQ(SB_THREAT_TYPE_SUBRESOURCE_FILTER, threat_);
-  expected.threat_pattern_type = ThreatPatternType::NONE;
-  EXPECT_EQ(expected, meta_);
-}
-
 TEST_F(SafeBrowsingApiHandlerUtilTest, SubresourceFilterSubTypes) {
   typedef SubresourceFilterLevel Level;
   typedef SubresourceFilterType Type;
@@ -218,21 +144,4 @@
   }
 }
 
-TEST_F(SafeBrowsingApiHandlerUtilTest, NoUnwantedSoftwareSubTypes) {
-  ThreatMetadata expected;
-
-  EXPECT_EQ(UMA_STATUS_MATCH,
-            ResetAndParseJson("{\"matches\":[{\"threat_type\":\"3\"}]}"));
-  EXPECT_EQ(SB_THREAT_TYPE_URL_UNWANTED, threat_);
-  expected.threat_pattern_type = ThreatPatternType::NONE;
-  EXPECT_EQ(expected, meta_);
-
-  EXPECT_EQ(UMA_STATUS_MATCH,
-            ResetAndParseJson("{\"matches\":[{\"threat_type\":\"3\", "
-                              "\"se_pattern_type\":\"junk\"}]}"));
-  EXPECT_EQ(SB_THREAT_TYPE_URL_UNWANTED, threat_);
-  expected.threat_pattern_type = ThreatPatternType::NONE;
-  EXPECT_EQ(expected, meta_);
-}
-
 }  // namespace safe_browsing
diff --git a/components/safe_browsing/android/safe_browsing_api_handler_util.cc b/components/safe_browsing/android/safe_browsing_api_handler_util.cc
index 92237db..0c4c81c 100644
--- a/components/safe_browsing/android/safe_browsing_api_handler_util.cc
+++ b/components/safe_browsing/android/safe_browsing_api_handler_util.cc
@@ -43,81 +43,6 @@
   UMA_THREAT_SUB_TYPE_MAX_VALUE
 };
 
-void ReportUmaThreatSubType(SBThreatType threat_type,
-                            UmaThreatSubType sub_type) {
-  if (threat_type == SB_THREAT_TYPE_URL_MALWARE) {
-    UMA_HISTOGRAM_ENUMERATION(
-        "SB2.RemoteCall.ThreatSubType.PotentiallyHarmfulApp", sub_type,
-        UMA_THREAT_SUB_TYPE_MAX_VALUE);
-  } else {
-    UMA_HISTOGRAM_ENUMERATION("SB2.RemoteCall.ThreatSubType.SocialEngineering",
-                              sub_type, UMA_THREAT_SUB_TYPE_MAX_VALUE);
-  }
-}
-
-// Parse the appropriate "*_pattern_type" key from the metadata.
-// Returns NONE if no pattern type was found.
-ThreatPatternType ParseThreatSubType(const base::DictionaryValue* match,
-                                     SBThreatType threat_type) {
-  if (threat_type == SB_THREAT_TYPE_URL_UNWANTED ||
-      threat_type == SB_THREAT_TYPE_SUBRESOURCE_FILTER ||
-      threat_type == SB_THREAT_TYPE_BILLING) {
-    return ThreatPatternType::NONE;
-  }
-
-  std::string pattern_key;
-  switch (threat_type) {
-    case SB_THREAT_TYPE_URL_MALWARE:
-      pattern_key = "pha_pattern_type";
-      break;
-    case SB_THREAT_TYPE_URL_PHISHING:
-      pattern_key = "se_pattern_type";
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-
-  std::string pattern_type;
-  if (!match->GetString(pattern_key, &pattern_type)) {
-    ReportUmaThreatSubType(threat_type, UMA_THREAT_SUB_TYPE_NOT_SET);
-    return ThreatPatternType::NONE;
-  }
-
-  if (threat_type == SB_THREAT_TYPE_URL_MALWARE) {
-    if (pattern_type == "LANDING") {
-      ReportUmaThreatSubType(
-          threat_type, UMA_THREAT_SUB_TYPE_POTENTIALLY_HALMFUL_APP_LANDING);
-      return ThreatPatternType::MALWARE_LANDING;
-    } else if (pattern_type == "DISTRIBUTION") {
-      ReportUmaThreatSubType(
-          threat_type,
-          UMA_THREAT_SUB_TYPE_POTENTIALLY_HALMFUL_APP_DISTRIBUTION);
-      return ThreatPatternType::MALWARE_DISTRIBUTION;
-    } else {
-      ReportUmaThreatSubType(threat_type, UMA_THREAT_SUB_TYPE_UNKNOWN);
-      return ThreatPatternType::NONE;
-    }
-  } else {
-    DCHECK(threat_type == SB_THREAT_TYPE_URL_PHISHING);
-    if (pattern_type == "SOCIAL_ENGINEERING_ADS") {
-      ReportUmaThreatSubType(threat_type,
-                             UMA_THREAT_SUB_TYPE_SOCIAL_ENGINEERING_ADS);
-      return ThreatPatternType::SOCIAL_ENGINEERING_ADS;
-    } else if (pattern_type == "SOCIAL_ENGINEERING_LANDING") {
-      ReportUmaThreatSubType(threat_type,
-                             UMA_THREAT_SUB_TYPE_SOCIAL_ENGINEERING_LANDING);
-      return ThreatPatternType::SOCIAL_ENGINEERING_LANDING;
-    } else if (pattern_type == "PHISHING") {
-      ReportUmaThreatSubType(threat_type, UMA_THREAT_SUB_TYPE_PHISHING);
-      return ThreatPatternType::PHISHING;
-    } else {
-      ReportUmaThreatSubType(threat_type, UMA_THREAT_SUB_TYPE_UNKNOWN);
-      return ThreatPatternType::NONE;
-    }
-  }
-}
-
 // Parse the optional "UserPopulation" key from the metadata.
 // Returns empty string if none was found.
 std::string ParseUserPopulation(const base::DictionaryValue* match) {
@@ -197,7 +122,7 @@
 // Valid examples:
 // {"matches":[{"threat_type":"5"}]}
 //   or
-// {"matches":[{"threat_type":"4", "pha_pattern_type":"LANDING"},
+// {"matches":[{"threat_type":"4"},
 //             {"threat_type":"5"}]}
 //   or
 // {"matches":[{"threat_type":"4", "UserPopulation":"YXNvZWZpbmFqO..."}]
@@ -251,8 +176,6 @@
     return UMA_STATUS_JSON_UNKNOWN_THREAT;
 
   // Fill in the metadata
-  metadata->threat_pattern_type =
-      ParseThreatSubType(worst_match, *worst_sb_threat_type);
   metadata->population_id = ParseUserPopulation(worst_match);
   if (*worst_sb_threat_type == SB_THREAT_TYPE_SUBRESOURCE_FILTER) {
     metadata->subresource_filter_match =
diff --git a/components/security_interstitials/content/BUILD.gn b/components/security_interstitials/content/BUILD.gn
index 19e15b2..17e4c0f 100644
--- a/components/security_interstitials/content/BUILD.gn
+++ b/components/security_interstitials/content/BUILD.gn
@@ -23,6 +23,8 @@
     "connection_help_ui.h",
     "content_metrics_helper.cc",
     "content_metrics_helper.h",
+    "https_only_mode_blocking_page.cc",
+    "https_only_mode_blocking_page.h",
     "insecure_form_blocking_page.cc",
     "insecure_form_blocking_page.h",
     "insecure_form_navigation_throttle.cc",
diff --git a/components/security_interstitials/content/https_only_mode_blocking_page.cc b/components/security_interstitials/content/https_only_mode_blocking_page.cc
new file mode 100644
index 0000000..b73af97
--- /dev/null
+++ b/components/security_interstitials/content/https_only_mode_blocking_page.cc
@@ -0,0 +1,117 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/security_interstitials/content/https_only_mode_blocking_page.h"
+
+#include "base/logging.h"
+#include "base/notreached.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/security_interstitials/content/security_interstitial_controller_client.h"
+#include "components/security_interstitials/core/common_string_util.h"
+#include "components/security_interstitials/core/pref_names.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace security_interstitials {
+
+// static
+const SecurityInterstitialPage::TypeID
+    HttpsOnlyModeBlockingPage::kTypeForTesting =
+        &HttpsOnlyModeBlockingPage::kTypeForTesting;
+
+HttpsOnlyModeBlockingPage::HttpsOnlyModeBlockingPage(
+    content::WebContents* web_contents,
+    const GURL& request_url,
+    std::unique_ptr<SecurityInterstitialControllerClient> controller_client)
+    : SecurityInterstitialPage(web_contents,
+                               request_url,
+                               std::move(controller_client)) {}
+
+HttpsOnlyModeBlockingPage::~HttpsOnlyModeBlockingPage() = default;
+
+SecurityInterstitialPage::TypeID
+HttpsOnlyModeBlockingPage::GetTypeForTesting() {
+  return HttpsOnlyModeBlockingPage::kTypeForTesting;
+}
+
+void HttpsOnlyModeBlockingPage::CommandReceived(const std::string& command) {
+  if (command == "\"pageLoadComplete\"") {
+    // content::WaitForRenderFrameReady sends this message when the page
+    // load completes. Ignore it.
+    return;
+  }
+  int cmd = 0;
+  bool retval = base::StringToInt(command, &cmd);
+  DCHECK(retval);
+  switch (cmd) {
+    case security_interstitials::CMD_DONT_PROCEED:
+      controller()->GoBack();
+      break;
+    case security_interstitials::CMD_PROCEED:
+      controller()->Proceed();
+      break;
+    case security_interstitials::CMD_DO_REPORT:
+    case security_interstitials::CMD_DONT_REPORT:
+    case security_interstitials::CMD_SHOW_MORE_SECTION:
+    case security_interstitials::CMD_OPEN_DATE_SETTINGS:
+    case security_interstitials::CMD_OPEN_REPORTING_PRIVACY:
+    case security_interstitials::CMD_OPEN_WHITEPAPER:
+    case security_interstitials::CMD_OPEN_HELP_CENTER:
+    case security_interstitials::CMD_RELOAD:
+    case security_interstitials::CMD_OPEN_DIAGNOSTIC:
+    case security_interstitials::CMD_OPEN_LOGIN:
+    case security_interstitials::CMD_REPORT_PHISHING_ERROR:
+      // Not supported by the HTTPS-only mode blocking page.
+      NOTREACHED() << "Unsupported command: " << command;
+      break;
+    case security_interstitials::CMD_ERROR:
+    case security_interstitials::CMD_TEXT_FOUND:
+    case security_interstitials::CMD_TEXT_NOT_FOUND:
+      // Commands are for testing.
+      break;
+  }
+}
+
+void HttpsOnlyModeBlockingPage::PopulateInterstitialStrings(
+    base::Value* load_time_data) {
+  PopulateValuesForSharedHTML(load_time_data);
+
+  load_time_data->SetStringKey(
+      "tabTitle", l10n_util::GetStringUTF16(IDS_HTTPS_ONLY_MODE_TITLE));
+  load_time_data->SetStringKey(
+      "heading", l10n_util::GetStringFUTF16(
+                     IDS_HTTPS_ONLY_MODE_HEADING,
+                     common_string_util::GetFormattedHostName(request_url())));
+  load_time_data->SetStringKey(
+      "primaryParagraph",
+      l10n_util::GetStringUTF16(IDS_HTTPS_ONLY_MODE_PRIMARY_PARAGRAPH));
+  load_time_data->SetStringKey(
+      "proceedButtonText",
+      l10n_util::GetStringUTF16(IDS_HTTPS_ONLY_MODE_SUBMIT_BUTTON));
+  load_time_data->SetStringKey(
+      "primaryButtonText",
+      l10n_util::GetStringUTF16(IDS_HTTPS_ONLY_MODE_BACK_BUTTON));
+  load_time_data->SetStringKey(
+      "optInLink",
+      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_SCOUT_REPORTING_AGREE));
+  load_time_data->SetStringKey(
+      "enhancedProtectionMessage",
+      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_ENHANCED_PROTECTION_MESSAGE));
+}
+
+void HttpsOnlyModeBlockingPage::PopulateValuesForSharedHTML(
+    base::Value* load_time_data) {
+  load_time_data->SetStringKey("type", "HTTPS_ONLY");
+  load_time_data->SetBoolKey("overridable", false);
+  load_time_data->SetBoolKey("hide_primary_button", false);
+  load_time_data->SetBoolKey("show_recurrent_error_paragraph", false);
+  load_time_data->SetStringKey("recurrentErrorParagraph", "");
+  load_time_data->SetStringKey("openDetails", "");
+  load_time_data->SetStringKey("explanationParagraph", "");
+  load_time_data->SetStringKey("finalParagraph", "");
+}
+
+}  // namespace security_interstitials
diff --git a/components/security_interstitials/content/https_only_mode_blocking_page.h b/components/security_interstitials/content/https_only_mode_blocking_page.h
new file mode 100644
index 0000000..fd1dda80
--- /dev/null
+++ b/components/security_interstitials/content/https_only_mode_blocking_page.h
@@ -0,0 +1,40 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_HTTPS_ONLY_MODE_BLOCKING_PAGE_H_
+#define COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_HTTPS_ONLY_MODE_BLOCKING_PAGE_H_
+
+#include "components/security_interstitials/content/security_interstitial_page.h"
+
+namespace security_interstitials {
+
+// Interstitial page object used for warnings shown when HTTPS-Only Mode fails
+// to upgrade a navigation to HTTPS.
+class HttpsOnlyModeBlockingPage : public SecurityInterstitialPage {
+ public:
+  HttpsOnlyModeBlockingPage(
+      content::WebContents* web_contents,
+      const GURL& request_url,
+      std::unique_ptr<SecurityInterstitialControllerClient> controller_client);
+
+  static const SecurityInterstitialPage::TypeID kTypeForTesting;
+  ~HttpsOnlyModeBlockingPage() override;
+
+  // SecurityInterstitialPage:
+  void OnInterstitialClosing() override {}
+  SecurityInterstitialPage::TypeID GetTypeForTesting() override;
+
+ protected:
+  // SecurityInterstitialPage:
+  void CommandReceived(const std::string& command) override;
+  void PopulateInterstitialStrings(base::Value* load_time_data) override;
+
+ private:
+  // Adds values required for shared interstitial HTML to |load_time_data|.
+  void PopulateValuesForSharedHTML(base::Value* load_time_data);
+};
+
+}  // namespace security_interstitials
+
+#endif  // COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_HTTPS_ONLY_MODE_BLOCKING_PAGE_H_
diff --git a/components/security_interstitials/content/security_blocking_page_factory.h b/components/security_interstitials/content/security_blocking_page_factory.h
index ad3720a..f87bba7e 100644
--- a/components/security_interstitials/content/security_blocking_page_factory.h
+++ b/components/security_interstitials/content/security_blocking_page_factory.h
@@ -12,6 +12,7 @@
 #include "components/security_interstitials/content/bad_clock_blocking_page.h"
 #include "components/security_interstitials/content/blocked_interception_blocking_page.h"
 #include "components/security_interstitials/content/captive_portal_blocking_page.h"
+#include "components/security_interstitials/content/https_only_mode_blocking_page.h"
 #include "components/security_interstitials/content/insecure_form_blocking_page.h"
 #include "components/security_interstitials/content/legacy_tls_blocking_page.h"
 #include "components/security_interstitials/content/mitm_software_blocking_page.h"
@@ -88,6 +89,10 @@
   CreateInsecureFormBlockingPage(content::WebContents* web_contents,
                                  const GURL& request_url) = 0;
 
+  virtual std::unique_ptr<security_interstitials::HttpsOnlyModeBlockingPage>
+  CreateHttpsOnlyModeBlockingPage(content::WebContents* web_contents,
+                                  const GURL& request_url) = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(SecurityBlockingPageFactory);
 };
diff --git a/components/security_interstitials/content/stateful_ssl_host_state_delegate.cc b/components/security_interstitials/content/stateful_ssl_host_state_delegate.cc
index 46d6780..94670c9 100644
--- a/components/security_interstitials/content/stateful_ssl_host_state_delegate.cc
+++ b/components/security_interstitials/content/stateful_ssl_host_state_delegate.cc
@@ -284,15 +284,6 @@
                                           content::WebContents* web_contents) {
   DCHECK(web_contents);
 
-  // If the appropriate flag is set, let requests on localhost go
-  // through even if there are certificate errors. Errors on localhost
-  // are unlikely to indicate actual security problems.
-  GURL url = GetSecureGURLForHost(host);
-  bool allow_localhost = base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kAllowInsecureLocalhost);
-  if (allow_localhost && net::IsLocalhost(url))
-    return ALLOWED;
-
   content::StoragePartition* storage_partition =
       browser_context_->GetStoragePartition(
           web_contents->GetMainFrame()->GetSiteInstance(),
@@ -312,6 +303,7 @@
     return DENIED;
   }
 
+  GURL url = GetSecureGURLForHost(host);
   std::unique_ptr<base::Value> value(
       host_content_settings_map_->GetWebsiteSetting(
           url, url, ContentSettingsType::SSL_CERT_DECISIONS, nullptr));
@@ -369,6 +361,17 @@
   return false;
 }
 
+// TODO(crbug.com/1218526): Hook up to content settings and expiration logic.
+void StatefulSSLHostStateDelegate::AllowHttpForHost(const std::string& host) {
+  allow_http_hosts_.insert(host);
+}
+
+// TODO(crbug.com/1218526): Hook up to content settings and expiration logic.
+bool StatefulSSLHostStateDelegate::IsHttpAllowedForHost(
+    const std::string& host) {
+  return base::Contains(allow_http_hosts_, host);
+}
+
 void StatefulSSLHostStateDelegate::RevokeUserAllowExceptions(
     const std::string& host) {
   GURL url = GetSecureGURLForHost(host);
diff --git a/components/security_interstitials/content/stateful_ssl_host_state_delegate.h b/components/security_interstitials/content/stateful_ssl_host_state_delegate.h
index ea26aac40..c7f9f2c 100644
--- a/components/security_interstitials/content/stateful_ssl_host_state_delegate.h
+++ b/components/security_interstitials/content/stateful_ssl_host_state_delegate.h
@@ -65,6 +65,8 @@
   bool DidHostRunInsecureContent(const std::string& host,
                                  int child_id,
                                  InsecureContentType content_type) override;
+  void AllowHttpForHost(const std::string& host) override;
+  bool IsHttpAllowedForHost(const std::string& host) override;
   void RevokeUserAllowExceptions(const std::string& host) override;
   bool HasAllowException(const std::string& host,
                          content::WebContents* web_contents) override;
@@ -159,6 +161,11 @@
   // to a certain threshold value.
   std::map<int /* error code */, int /* count */> recurrent_errors_;
 
+  // Tracks sites that are allowed to load over HTTP when HTTPS-Only Mode is
+  // enabled. Allowed hosts are exact hostname matches -- subdomains of a host
+  // on the allowlist must be separately allowlisted.
+  std::set<std::string /* host */> allow_http_hosts_;
+
   DISALLOW_COPY_AND_ASSIGN(StatefulSSLHostStateDelegate);
 
   int recurrent_interstitial_threshold_for_testing;
diff --git a/components/security_interstitials/core/browser/resources/interstitial_httpsonly.css b/components/security_interstitials/core/browser/resources/interstitial_httpsonly.css
new file mode 100644
index 0000000..c65241a
--- /dev/null
+++ b/components/security_interstitials/core/browser/resources/interstitial_httpsonly.css
@@ -0,0 +1,21 @@
+/* Copyright 2021 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+.https-only #main-content a {
+  color: var(--google-blue-700);
+  text-decoration: none;
+}
+
+.https-only .icon {
+  background-image: -webkit-image-set(
+    url(images/1x/triangle_red.png) 1x,
+    url(images/2x/triangle_red.png) 2x);
+  filter: grayscale(1);
+}
+
+@media (prefers-color-scheme: dark) {
+  .https-only .icon {
+    filter: invert(1);
+  }
+}
diff --git a/components/security_interstitials/core/browser/resources/interstitial_large.html b/components/security_interstitials/core/browser/resources/interstitial_large.html
index 07fd81e..2dfa973e 100644
--- a/components/security_interstitials/core/browser/resources/interstitial_large.html
+++ b/components/security_interstitials/core/browser/resources/interstitial_large.html
@@ -13,6 +13,7 @@
   <link rel="stylesheet" href="../../common/resources/interstitial_common.css">
   <link rel="stylesheet" href="interstitial_badclock.css">
   <link rel="stylesheet" href="interstitial_captiveportal.css">
+  <link rel="stylesheet" href="interstitial_httpsonly.css">
   <link rel="stylesheet" href="interstitial_insecureform.css">
   <link rel="stylesheet" href="interstitial_lookalikeurl.css">
   <link rel="stylesheet" href="interstitial_safebrowsing.css">
diff --git a/components/security_interstitials/core/browser/resources/interstitial_large.js b/components/security_interstitials/core/browser/resources/interstitial_large.js
index 2c1abd2..f33beb5 100644
--- a/components/security_interstitials/core/browser/resources/interstitial_large.js
+++ b/components/security_interstitials/core/browser/resources/interstitial_large.js
@@ -76,6 +76,7 @@
   const blockedInterception = interstitialType === 'BLOCKED_INTERCEPTION';
   const legacyTls = interstitialType == 'LEGACY_TLS';
   const insecureForm = interstitialType == 'INSECURE_FORM';
+  const httpsOnly = interstitialType == 'HTTPS_ONLY';
   const hidePrimaryButton = loadTimeData.getBoolean('hide_primary_button');
   const showRecurrentErrorParagraph = loadTimeData.getBoolean(
     'show_recurrent_error_paragraph');
@@ -94,6 +95,8 @@
     $('body').classList.add('lookalike-url');
   } else if (insecureForm) {
     $('body').classList.add('insecure-form');
+  } else if (httpsOnly) {
+    $('body').classList.add('https-only');
   } else {
     $('body').classList.add('safe-browsing');
     // Override the default theme color.
@@ -131,6 +134,7 @@
         case 'ORIGIN_POLICY':
           sendCommand(SecurityInterstitialCommandId.CMD_DONT_PROCEED);
           break;
+        case 'HTTPS_ONLY':
         case 'INSECURE_FORM':
         case 'LOOKALIKE':
           sendCommand(SecurityInterstitialCommandId.CMD_DONT_PROCEED);
@@ -142,7 +146,7 @@
     });
   }
 
-  if (lookalike || insecureForm) {
+  if (lookalike || insecureForm || httpsOnly) {
     const proceedButton = 'proceed-button';
     $(proceedButton).classList.remove(HIDDEN_CLASS);
     $(proceedButton).textContent = loadTimeData.getString('proceedButtonText');
@@ -199,9 +203,9 @@
     });
   }
 
-  if (captivePortal || billing || lookalike || insecureForm) {
-    // Captive portal, billing, lookalike pages, and insecure form
-    // interstitials don't have details buttons.
+  if (captivePortal || billing || lookalike || insecureForm || httpsOnly) {
+    // Captive portal, billing, lookalike pages, insecure form, and
+    // HTTPS only mode interstitials don't have details buttons.
     $('details-button').classList.add('hidden');
   } else {
     $('details-button')
diff --git a/components/security_interstitials/core/browser/resources/list_of_interstitials.html b/components/security_interstitials/core/browser/resources/list_of_interstitials.html
index 9fb6c7d..1f9005e 100644
--- a/components/security_interstitials/core/browser/resources/list_of_interstitials.html
+++ b/components/security_interstitials/core/browser/resources/list_of_interstitials.html
@@ -142,5 +142,11 @@
       <a href="insecure_form">Insecure Form</a>
     </li>
   </ul>
+  <h3>HTTPS-Only Mode Warnings</h3>
+  <ul>
+    <li>
+      <a href="https_only">HTTPS-Only Mode</a>
+    </li>
+  </ul>
 </body>
 </html>
diff --git a/components/security_interstitials/core/common/resources/interstitial_common.css b/components/security_interstitials/core/common/resources/interstitial_common.css
index 8137dbe..620221f 100644
--- a/components/security_interstitials/core/common/resources/interstitial_common.css
+++ b/components/security_interstitials/core/common/resources/interstitial_common.css
@@ -22,6 +22,7 @@
 
 .bad-clock button,
 .captive-portal button,
+.https-only button,
 .insecure-form button,
 .lookalike-url button,
 .main-frame-blocked button,
diff --git a/components/security_interstitials_strings.grdp b/components/security_interstitials_strings.grdp
index a9a8054..3e14630 100644
--- a/components/security_interstitials_strings.grdp
+++ b/components/security_interstitials_strings.grdp
@@ -533,4 +533,21 @@
     Send anyway
   </message>
 
+  <!-- HTTPS-Only Mode interstitial-->
+  <message name="IDS_HTTPS_ONLY_MODE_TITLE" desc="Tab title for HTTPS-only mode warning">
+    Site is not secure
+  </message>
+  <message name="IDS_HTTPS_ONLY_MODE_HEADING" desc="Large heading of the warning shown when an http only site is loaded with https-only mode active">
+    The connection to <ph name="SITE">$1<ex>example.com</ex></ph> is not secure
+  </message>
+  <message name="IDS_HTTPS_ONLY_MODE_PRIMARY_PARAGRAPH" desc="Main paragraph of the HTTPS-Only Mode warning. This warning is shown when the browser tries to upgrade a navigation to a site to HTTPS but the site does not support HTTPS.">
+    You are seeing this warning because this site does not support HTTPS.
+  </message>
+  <message name="IDS_HTTPS_ONLY_MODE_BACK_BUTTON" desc="Text for the button in the HTTPS-only mode warning that takes the user back to the previous page">
+    Go back
+  </message>
+  <message name="IDS_HTTPS_ONLY_MODE_SUBMIT_BUTTON" desc="Text for the button in the HTTPS-only mode warning that bypasses the warning and loads the page">
+    Continue to site
+  </message>
+
 </grit-part>
diff --git a/components/security_interstitials_strings_grdp/IDS_HTTPS_ONLY_MODE_BACK_BUTTON.png.sha1 b/components/security_interstitials_strings_grdp/IDS_HTTPS_ONLY_MODE_BACK_BUTTON.png.sha1
new file mode 100644
index 0000000..c29e533
--- /dev/null
+++ b/components/security_interstitials_strings_grdp/IDS_HTTPS_ONLY_MODE_BACK_BUTTON.png.sha1
@@ -0,0 +1 @@
+fdba6d2a58dffe159f4434abf99af823cc2b43e8
\ No newline at end of file
diff --git a/components/security_interstitials_strings_grdp/IDS_HTTPS_ONLY_MODE_HEADING.png.sha1 b/components/security_interstitials_strings_grdp/IDS_HTTPS_ONLY_MODE_HEADING.png.sha1
new file mode 100644
index 0000000..c29e533
--- /dev/null
+++ b/components/security_interstitials_strings_grdp/IDS_HTTPS_ONLY_MODE_HEADING.png.sha1
@@ -0,0 +1 @@
+fdba6d2a58dffe159f4434abf99af823cc2b43e8
\ No newline at end of file
diff --git a/components/security_interstitials_strings_grdp/IDS_HTTPS_ONLY_MODE_PRIMARY_PARAGRAPH.png.sha1 b/components/security_interstitials_strings_grdp/IDS_HTTPS_ONLY_MODE_PRIMARY_PARAGRAPH.png.sha1
new file mode 100644
index 0000000..c29e533
--- /dev/null
+++ b/components/security_interstitials_strings_grdp/IDS_HTTPS_ONLY_MODE_PRIMARY_PARAGRAPH.png.sha1
@@ -0,0 +1 @@
+fdba6d2a58dffe159f4434abf99af823cc2b43e8
\ No newline at end of file
diff --git a/components/security_interstitials_strings_grdp/IDS_HTTPS_ONLY_MODE_SUBMIT_BUTTON.png.sha1 b/components/security_interstitials_strings_grdp/IDS_HTTPS_ONLY_MODE_SUBMIT_BUTTON.png.sha1
new file mode 100644
index 0000000..c29e533
--- /dev/null
+++ b/components/security_interstitials_strings_grdp/IDS_HTTPS_ONLY_MODE_SUBMIT_BUTTON.png.sha1
@@ -0,0 +1 @@
+fdba6d2a58dffe159f4434abf99af823cc2b43e8
\ No newline at end of file
diff --git a/components/security_interstitials_strings_grdp/IDS_HTTPS_ONLY_MODE_TITLE.png.sha1 b/components/security_interstitials_strings_grdp/IDS_HTTPS_ONLY_MODE_TITLE.png.sha1
new file mode 100644
index 0000000..c29e533
--- /dev/null
+++ b/components/security_interstitials_strings_grdp/IDS_HTTPS_ONLY_MODE_TITLE.png.sha1
@@ -0,0 +1 @@
+fdba6d2a58dffe159f4434abf99af823cc2b43e8
\ No newline at end of file
diff --git a/components/services/app_service/public/mojom/types.mojom b/components/services/app_service/public/mojom/types.mojom
index aa800919..aac5e06 100644
--- a/components/services/app_service/public/mojom/types.mojom
+++ b/components/services/app_service/public/mojom/types.mojom
@@ -105,6 +105,7 @@
   kRemote,                // Remote app.
   kBorealis,              // Borealis app, see go/borealis-app.
   kSystemWeb,             // System web app.
+  kStandaloneBrowserExtension,  // Extension based apps hosted in Lacros.
 };
 
 // Whether an app is ready to launch, i.e. installed.
@@ -479,4 +480,4 @@
   kBrowser,
   // Opens in a tabbed app window
   kTabbedWindow,
-};
\ No newline at end of file
+};
diff --git a/components/services/storage/public/cpp/buckets/BUILD.gn b/components/services/storage/public/cpp/buckets/BUILD.gn
index 471b1de..61e4a671 100644
--- a/components/services/storage/public/cpp/buckets/BUILD.gn
+++ b/components/services/storage/public/cpp/buckets/BUILD.gn
@@ -10,7 +10,6 @@
 
   public_deps = [
     "//base",
-    "//base/util/type_safety",
     "//third_party/blink/public/common",
   ]
 
diff --git a/components/services/storage/public/cpp/buckets/bucket_id.h b/components/services/storage/public/cpp/buckets/bucket_id.h
index f34a6d2..dafbac1 100644
--- a/components/services/storage/public/cpp/buckets/bucket_id.h
+++ b/components/services/storage/public/cpp/buckets/bucket_id.h
@@ -5,13 +5,13 @@
 #ifndef COMPONENTS_SERVICES_STORAGE_PUBLIC_CPP_BUCKETS_BUCKET_ID_H_
 #define COMPONENTS_SERVICES_STORAGE_PUBLIC_CPP_BUCKETS_BUCKET_ID_H_
 
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 
 namespace storage {
 
 // Type for the Storage Bucket ID.
 // IDs will always be generated by SQLite, and all valid BucketIds are positive.
-using BucketId = util::IdType64<class BucketTag>;
+using BucketId = base::IdType64<class BucketTag>;
 
 }  // namespace storage
 
diff --git a/components/services/storage/public/cpp/buckets/bucket_info.h b/components/services/storage/public/cpp/buckets/bucket_info.h
index e3cd0bd..9e8f337 100644
--- a/components/services/storage/public/cpp/buckets/bucket_info.h
+++ b/components/services/storage/public/cpp/buckets/bucket_info.h
@@ -6,7 +6,6 @@
 #define COMPONENTS_SERVICES_STORAGE_PUBLIC_CPP_BUCKETS_BUCKET_INFO_H_
 
 #include "base/time/time.h"
-#include "base/util/type_safety/id_type.h"
 #include "components/services/storage/public/cpp/buckets/bucket_id.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h"
diff --git a/components/sync/trusted_vault/standalone_trusted_vault_backend.cc b/components/sync/trusted_vault/standalone_trusted_vault_backend.cc
index 5a93af29..2c15bb8 100644
--- a/components/sync/trusted_vault/standalone_trusted_vault_backend.cc
+++ b/components/sync/trusted_vault/standalone_trusted_vault_backend.cc
@@ -16,6 +16,7 @@
 #include "base/files/file_util.h"
 #include "base/files/important_file_writer.h"
 #include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/sequence_checker.h"
 #include "base/stl_util.h"
 #include "base/time/clock.h"
@@ -244,7 +245,16 @@
     per_user_vault = data_.add_user();
     per_user_vault->set_gaia_id(primary_account->gaia);
   }
-  MaybeRegisterDevice();
+
+  const absl::optional<DeviceRegistrationStateForUMA> registration_state =
+      MaybeRegisterDevice();
+
+  if (registration_state.has_value() &&
+      !device_registration_state_recorded_to_uma_) {
+    device_registration_state_recorded_to_uma_ = true;
+    base::UmaHistogramEnumeration("Sync.TrustedVaultDeviceRegistrationState",
+                                  *registration_state);
+  }
 
   if (pending_trusted_recovery_method_.has_value()) {
     PendingTrustedRecoveryMethod recovery_method =
@@ -410,16 +420,18 @@
   clock_ = clock;
 }
 
-void StandaloneTrustedVaultBackend::MaybeRegisterDevice() {
+absl::optional<StandaloneTrustedVaultBackend::DeviceRegistrationStateForUMA>
+StandaloneTrustedVaultBackend::MaybeRegisterDevice() {
   // TODO(crbug.com/1102340): in case of transient failure this function is
   // likely to be not called until the browser restart; implement retry logic.
   if (!connection_) {
     // Feature disabled.
-    return;
+    return absl::nullopt;
   }
+
   if (!primary_account_.has_value()) {
     // Device registration is supported only for |primary_account_|.
-    return;
+    return absl::nullopt;
   }
 
   // |per_user_vault| must be created before calling this function.
@@ -432,19 +444,21 @@
           switches::kAllowSilentTrustedVaultDeviceRegistration)) {
     // Either vault key with known version should be available or registration
     // without it should be allowed through feature flag.
-    return;
+    return absl::nullopt;
   }
+
+  if (per_user_vault->local_device_registration_info().device_registered()) {
+    return DeviceRegistrationStateForUMA::kAlreadyRegistered;
+  }
+
   if (per_user_vault->keys_are_stale()) {
     // Client already knows that existing vault keys (or their absence) isn't
     // sufficient for device registration. Fresh keys should be obtained first.
-    return;
+    return DeviceRegistrationStateForUMA::kLocalKeysAreStale;
   }
-  if (per_user_vault->local_device_registration_info().device_registered()) {
-    // Device is already registered.
-    return;
-  }
+
   if (AreConnectionRequestsThrottled()) {
-    return;
+    return DeviceRegistrationStateForUMA::kThrottledClientSide;
   }
 
   std::unique_ptr<SecureBoxKeyPair> key_pair;
@@ -453,12 +467,11 @@
         /*private_key_bytes=*/ProtoStringToBytes(
             per_user_vault->local_device_registration_info()
                 .private_key_material()));
-    if (!key_pair) {
-      // Device key is corrupted.
-      // TODO(crbug.com/1102340): consider generation of new key in this case.
-      return;
-    }
-  } else {
+  }
+
+  const bool had_generated_key_pair = key_pair != nullptr;
+
+  if (!key_pair) {
     key_pair = SecureBoxKeyPair::GenerateRandom();
     // It's possible that device will be successfully registered, but the client
     // won't persist this state (for example response doesn't reach the client
@@ -494,6 +507,11 @@
   }
 
   DCHECK(ongoing_connection_request_);
+
+  return had_generated_key_pair ? DeviceRegistrationStateForUMA::
+                                      kAttemptingRegistrationWithExistingKeyPair
+                                : DeviceRegistrationStateForUMA::
+                                      kAttemptingRegistrationWithNewKeyPair;
 }
 
 void StandaloneTrustedVaultBackend::OnDeviceRegistered(
diff --git a/components/sync/trusted_vault/standalone_trusted_vault_backend.h b/components/sync/trusted_vault/standalone_trusted_vault_backend.h
index b06bf41..0317503 100644
--- a/components/sync/trusted_vault/standalone_trusted_vault_backend.h
+++ b/components/sync/trusted_vault/standalone_trusted_vault_backend.h
@@ -117,6 +117,18 @@
 
   void SetClockForTesting(base::Clock* clock);
 
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+  // Exposed publicly for testing.
+  enum class DeviceRegistrationStateForUMA {
+    kAlreadyRegistered = 0,
+    kLocalKeysAreStale = 1,
+    kThrottledClientSide = 2,
+    kAttemptingRegistrationWithNewKeyPair = 3,
+    kAttemptingRegistrationWithExistingKeyPair = 4,
+    kMaxValue = kAttemptingRegistrationWithExistingKeyPair,
+  };
+
  private:
   friend class base::RefCountedThreadSafe<StandaloneTrustedVaultBackend>;
 
@@ -127,8 +139,11 @@
   sync_pb::LocalTrustedVaultPerUser* FindUserVault(const std::string& gaia_id);
 
   // Attempts to register device in case it's not yet registered and currently
-  // available local data is sufficient to do it.
-  void MaybeRegisterDevice();
+  // available local data is sufficient to do it. For the cases where
+  // registration is desirable (i.e. feature toggle enabled and user signed in),
+  // it returns an enum representing the registration state, intended to be used
+  // for metric recording. Otherwise it returns nullopt.
+  absl::optional<DeviceRegistrationStateForUMA> MaybeRegisterDevice();
 
   // Called when device registration for |gaia_id| is completed (either
   // successfully or not). |data_| must contain LocalTrustedVaultPerUser for
@@ -219,6 +234,8 @@
   base::Clock* clock_;
 
   std::vector<uint8_t> last_added_recovery_method_public_key_for_testing_;
+
+  bool device_registration_state_recorded_to_uma_ = false;
 };
 
 }  // namespace syncer
diff --git a/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc b/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc
index 9be9462..8239be7 100644
--- a/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc
+++ b/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
@@ -130,6 +131,17 @@
       : file_path_(
             CreateUniqueTempDir(&temp_dir_)
                 .Append(base::FilePath(FILE_PATH_LITERAL("some_file")))) {
+    clock_.SetNow(base::Time::Now());
+    ResetBackend();
+  }
+
+  ~StandaloneTrustedVaultBackendTest() override = default;
+
+  void SetUp() override { OSCryptMocker::SetUp(); }
+
+  void TearDown() override { OSCryptMocker::TearDown(); }
+
+  void ResetBackend() {
     auto delegate = std::make_unique<testing::NiceMock<MockDelegate>>();
     delegate_ = delegate.get();
 
@@ -140,7 +152,6 @@
     backend_ = base::MakeRefCounted<StandaloneTrustedVaultBackend>(
         file_path_, std::move(delegate), std::move(connection));
     backend_->SetClockForTesting(&clock_);
-    clock_.SetNow(base::Time::Now());
 
     // To avoid DCHECK failures in tests that exercise SetPrimaryAccount(),
     // return non-null for RegisterAuthenticationFactor(). This registration
@@ -155,12 +166,6 @@
         }));
   }
 
-  ~StandaloneTrustedVaultBackendTest() override = default;
-
-  void SetUp() override { OSCryptMocker::SetUp(); }
-
-  void TearDown() override { OSCryptMocker::TearDown(); }
-
   MockTrustedVaultConnection* connection() { return connection_; }
 
   base::SimpleTestClock* clock() { return &clock_; }
@@ -472,8 +477,15 @@
       });
 
   // Setting the primary account will trigger device registration.
+  base::HistogramTester histogram_tester;
   backend()->SetPrimaryAccount(account_info);
   ASSERT_FALSE(device_registration_callback.is_null());
+  histogram_tester.ExpectUniqueSample(
+      "Sync.TrustedVaultDeviceRegistrationState",
+      /*sample=*/
+      StandaloneTrustedVaultBackend::DeviceRegistrationStateForUMA::
+          kAttemptingRegistrationWithNewKeyPair,
+      /*expected_bucket_count=*/1);
 
   // Pretend that the registration completed successfully.
   std::move(device_registration_callback)
@@ -493,6 +505,88 @@
 }
 
 TEST_F(StandaloneTrustedVaultBackendTest,
+       ShouldNotRegisterDeviceIfLocalKeysAreStale) {
+  const CoreAccountInfo account_info = MakeAccountInfoWithGaiaId("user");
+  const std::vector<uint8_t> kVaultKey = {1, 2, 3};
+  const int kLastKeyVersion = 1;
+
+  backend()->StoreKeys(account_info.gaia, {kVaultKey}, kLastKeyVersion);
+  ASSERT_TRUE(backend()->MarkLocalKeysAsStale(account_info));
+
+  EXPECT_CALL(*connection(), RegisterAuthenticationFactor(_, _, _, _, _, _, _))
+      .Times(0);
+  EXPECT_CALL(*connection(), RegisterDeviceWithoutKeys(_, _, _)).Times(0);
+
+  base::HistogramTester histogram_tester;
+  backend()->SetPrimaryAccount(account_info);
+
+  histogram_tester.ExpectUniqueSample(
+      "Sync.TrustedVaultDeviceRegistrationState",
+      /*sample=*/
+      StandaloneTrustedVaultBackend::DeviceRegistrationStateForUMA::
+          kLocalKeysAreStale,
+      /*expected_bucket_count=*/1);
+}
+
+TEST_F(StandaloneTrustedVaultBackendTest,
+       ShouldNotRegisterDeviceIfAlreadyRegistered) {
+  const CoreAccountInfo account_info = MakeAccountInfoWithGaiaId("user");
+  const std::vector<uint8_t> kVaultKey = {1, 2, 3};
+  const int kLastKeyVersion = 1;
+
+  TrustedVaultConnection::RegisterAuthenticationFactorCallback
+      device_registration_callback;
+  ON_CALL(*connection(),
+          RegisterAuthenticationFactor(
+              Eq(account_info), ElementsAre(kVaultKey), kLastKeyVersion, _,
+              AuthenticationFactorType::kPhysicalDevice,
+              /*authentication_factor_type_hint=*/Eq(absl::nullopt), _))
+      .WillByDefault(
+          [&](const CoreAccountInfo&, const std::vector<std::vector<uint8_t>>&,
+              int, const SecureBoxPublicKey& device_public_key,
+              AuthenticationFactorType, absl::optional<int>,
+              TrustedVaultConnection::RegisterAuthenticationFactorCallback
+                  callback) {
+            device_registration_callback = std::move(callback);
+            return std::make_unique<TrustedVaultConnection::Request>();
+          });
+
+  backend()->StoreKeys(account_info.gaia, {kVaultKey}, kLastKeyVersion);
+  backend()->SetPrimaryAccount(account_info);
+  ASSERT_FALSE(device_registration_callback.is_null());
+  std::move(device_registration_callback)
+      .Run(TrustedVaultRegistrationStatus::kSuccess);
+
+  // Now the device should be registered.
+  ASSERT_TRUE(backend()
+                  ->GetDeviceRegistrationInfoForTesting(account_info.gaia)
+                  .device_registered());
+
+  // Mimic a restart. The device should remain registered.
+  ResetBackend();
+  backend()->ReadDataFromDisk();
+
+  ASSERT_TRUE(backend()
+                  ->GetDeviceRegistrationInfoForTesting(account_info.gaia)
+                  .device_registered());
+
+  // The device should not register again.
+  EXPECT_CALL(*connection(), RegisterAuthenticationFactor(_, _, _, _, _, _, _))
+      .Times(0);
+  EXPECT_CALL(*connection(), RegisterDeviceWithoutKeys(_, _, _)).Times(0);
+
+  base::HistogramTester histogram_tester;
+  backend()->SetPrimaryAccount(account_info);
+
+  histogram_tester.ExpectUniqueSample(
+      "Sync.TrustedVaultDeviceRegistrationState",
+      /*sample=*/
+      StandaloneTrustedVaultBackend::DeviceRegistrationStateForUMA::
+          kAlreadyRegistered,
+      /*expected_bucket_count=*/1);
+}
+
+TEST_F(StandaloneTrustedVaultBackendTest,
        ShouldThrottleAndUnthrottleDeviceRegistration) {
   const CoreAccountInfo account_info = MakeAccountInfoWithGaiaId("user");
   const std::vector<uint8_t> kVaultKey = {1, 2, 3};
@@ -512,8 +606,6 @@
             return std::make_unique<TrustedVaultConnection::Request>();
           });
 
-  clock()->SetNow(base::Time::Now());
-
   EXPECT_CALL(*connection(), RegisterAuthenticationFactor(_, _, _, _, _, _, _));
   // Setting the primary account will trigger device registration.
   backend()->SetPrimaryAccount(account_info);
@@ -524,25 +616,35 @@
   std::move(device_registration_callback)
       .Run(TrustedVaultRegistrationStatus::kOtherError);
 
-  // Following request should be throttled.
-  device_registration_callback =
-      TrustedVaultConnection::RegisterAuthenticationFactorCallback();
+  // Mimic a restart to trigger device registration attempt, which should remain
+  // throttled.
+  base::HistogramTester histogram_tester;
+  ResetBackend();
   EXPECT_CALL(*connection(), RegisterAuthenticationFactor(_, _, _, _, _, _, _))
       .Times(0);
-  // Reset and set primary account to trigger device registration attempt.
-  backend()->SetPrimaryAccount(absl::nullopt);
+  backend()->ReadDataFromDisk();
   backend()->SetPrimaryAccount(account_info);
-  EXPECT_TRUE(device_registration_callback.is_null());
-  Mock::VerifyAndClearExpectations(connection());
+  histogram_tester.ExpectUniqueSample(
+      "Sync.TrustedVaultDeviceRegistrationState",
+      /*sample=*/
+      StandaloneTrustedVaultBackend::DeviceRegistrationStateForUMA::
+          kThrottledClientSide,
+      /*expected_bucket_count=*/1);
 
-  // Advance time to pass the throttling duration and trigger another attempt.
-  clock()->Advance(switches::kTrustedVaultServiceThrottlingDuration.Get());
-
+  // Mimic a restart after sufficient time has passed, to trigger another device
+  // registration attempt, which should now be unthrottled.
+  base::HistogramTester histogram_tester2;
+  ResetBackend();
   EXPECT_CALL(*connection(), RegisterAuthenticationFactor(_, _, _, _, _, _, _));
-  // Reset and set primary account to trigger device registration attempt.
-  backend()->SetPrimaryAccount(absl::nullopt);
+  clock()->Advance(switches::kTrustedVaultServiceThrottlingDuration.Get());
+  backend()->ReadDataFromDisk();
   backend()->SetPrimaryAccount(account_info);
-  EXPECT_FALSE(device_registration_callback.is_null());
+  histogram_tester2.ExpectUniqueSample(
+      "Sync.TrustedVaultDeviceRegistrationState",
+      /*sample=*/
+      StandaloneTrustedVaultBackend::DeviceRegistrationStateForUMA::
+          kAttemptingRegistrationWithExistingKeyPair,
+      /*expected_bucket_count=*/1);
 }
 
 // System time can be changed to the past and if this situation not handled,
diff --git a/components/viz/common/quads/aggregated_render_pass.h b/components/viz/common/quads/aggregated_render_pass.h
index cbd7c9e..24f6781 100644
--- a/components/viz/common/quads/aggregated_render_pass.h
+++ b/components/viz/common/quads/aggregated_render_pass.h
@@ -14,7 +14,7 @@
 #include "base/callback.h"
 #include "base/hash/hash.h"
 #include "base/macros.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "cc/base/list_container.h"
 #include "cc/paint/filter_operations.h"
 #include "components/viz/common/quads/draw_quad.h"
@@ -33,7 +33,7 @@
 class CompositorRenderPassDrawQuad;
 class AggregatedRenderPassDrawQuad;
 
-using AggregatedRenderPassId = util::IdTypeU64<AggregatedRenderPass>;
+using AggregatedRenderPassId = base::IdTypeU64<AggregatedRenderPass>;
 
 // This class represents a render pass that is a result of aggregating render
 // passes from all of the relevant surfaces. It is _not_ mojo-serializable since
diff --git a/components/viz/common/quads/compositor_render_pass.h b/components/viz/common/quads/compositor_render_pass.h
index 65e9331..f2b8464 100644
--- a/components/viz/common/quads/compositor_render_pass.h
+++ b/components/viz/common/quads/compositor_render_pass.h
@@ -14,7 +14,7 @@
 #include "base/callback.h"
 #include "base/hash/hash.h"
 #include "base/macros.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "cc/base/list_container.h"
 #include "cc/paint/filter_operations.h"
 #include "components/viz/common/quads/draw_quad.h"
@@ -43,7 +43,7 @@
 class CompositorRenderPass;
 class CompositorRenderPassDrawQuad;
 
-using CompositorRenderPassId = util::IdTypeU64<CompositorRenderPass>;
+using CompositorRenderPassId = base::IdTypeU64<CompositorRenderPass>;
 
 // This class represents a render pass that is submitted from the UI or renderer
 // compositor to viz. It is mojo-serializable and typically has a unique
diff --git a/components/viz/common/resources/resource_id.h b/components/viz/common/resources/resource_id.h
index 7bb9859..99ce85f 100644
--- a/components/viz/common/resources/resource_id.h
+++ b/components/viz/common/resources/resource_id.h
@@ -12,7 +12,7 @@
 
 #include "base/check_op.h"
 #include "base/containers/flat_set.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 
 namespace viz {
 
@@ -20,7 +20,7 @@
 
 // Note that if you need to generate new ResourceIds, please use
 // ResourceIdGenerator below, since it will skip generating reserved ids.
-using ResourceId = util::IdTypeU32<ResourceIdTypeMarker>;
+using ResourceId = base::IdTypeU32<ResourceIdTypeMarker>;
 using ResourceIdSet = base::flat_set<ResourceId>;
 constexpr ResourceId kInvalidResourceId(0);
 constexpr ResourceId kVizReservedRangeStartId(
diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc
index 2f14ea8..db57099 100644
--- a/components/viz/service/display/gl_renderer.cc
+++ b/components/viz/service/display/gl_renderer.cc
@@ -4418,40 +4418,39 @@
   if (!sqs->quad_to_target_transform.Preserves2dAxisAlignment())
     return false;
 
-  // If no blending is needed for the quad, then fast draw can be safely used.
-  if (!quad->ShouldDrawWithBlending() && SkColorGetA(quad->color) == 255)
+  // When no blending is needed, glClear can be used.
+  SkBlendMode blend_mode = quad->shared_quad_state->blend_mode;
+  if (blend_mode == SkBlendMode::kSrc)
     return true;
 
-  // It is safe to use glClearColor with alpha blending when the render
-  // pass has transparent background because the blending happens against
-  // (0, 0, 0, 0) which is the same as replacing the destination color & alpha.
-  // However, if the render pass does not have a transparent background, using
-  // glClear with a color that has alpha or opacity, would end up punching an
-  // unwanted hole in the frame buffer.
-  if (!current_frame()->current_render_pass->has_transparent_background)
-    return false;
+  if (blend_mode == SkBlendMode::kSrcOver) {
+    // Blending will replace destination color and alpha if the quad is opaque.
+    if (SkColorGetA(quad->color) == 255 &&
+        quad->shared_quad_state->opacity >= 1.0f) {
+      return true;
+    }
 
-  // If the color has any alpha and blending is needed, ensure the blend mode
-  // allows replacing destination color & alpha.
-  const bool is_translucent =
-      SkColorGetA(quad->color) != 255 || quad->shared_quad_state->opacity < 1.f;
-  if (is_translucent &&
-      !(quad->shared_quad_state->blend_mode == SkBlendMode::kSrc ||
-        quad->shared_quad_state->blend_mode == SkBlendMode::kSrcOver)) {
-    return false;
+    // It is safe to use glClearColor with alpha blending when the render
+    // pass has transparent background and nothing has drawn to the same rect
+    // area because the blending happens against (0, 0, 0, 0) which is the same
+    // as replacing the destination color & alpha.
+    if (!current_frame()->current_render_pass->has_transparent_background)
+      return false;
+
+    gfx::RectF quad_rect_in_target(quad->visible_rect);
+    sqs->quad_to_target_transform.TransformRect(&quad_rect_in_target);
+    const gfx::Rect quad_rect_in_target_rounded =
+        gfx::ToRoundedRect(quad_rect_in_target);
+
+    // If the quad does not intersect any region that has already been drawn
+    // to, then blending is not an issue and fast draw path can be used.
+    for (const auto& rect : drawn_rects_)
+      if (quad_rect_in_target_rounded.Intersects(rect))
+        return false;
+    return true;
   }
 
-  gfx::RectF quad_rect_in_target(quad->visible_rect);
-  sqs->quad_to_target_transform.TransformRect(&quad_rect_in_target);
-  const gfx::Rect quad_rect_in_target_rounded =
-      gfx::ToRoundedRect(quad_rect_in_target);
-
-  // If the quad does not intersect with any region that has already been drawn
-  // to, then blending is not an issue and fast draw path can be used.
-  for (const auto& rect : drawn_rects_)
-    if (quad_rect_in_target_rounded.Intersects(rect))
-      return false;
-  return true;
+  return false;
 }
 
 void GLRenderer::AllocateRenderPassResourceIfNeeded(
diff --git a/components/viz/service/display/gl_renderer_unittest.cc b/components/viz/service/display/gl_renderer_unittest.cc
index 8037c66..9365f2e 100644
--- a/components/viz/service/display/gl_renderer_unittest.cc
+++ b/components/viz/service/display/gl_renderer_unittest.cc
@@ -3389,8 +3389,7 @@
       gfx::Transform(), cc::FilterOperations());
   root_pass->damage_rect = root_pass_damage_rect;
 
-  cc::AddQuad(root_pass, quad_rect_1, SK_ColorRED);
-  root_pass->quad_list.back()->needs_blending = true;
+  cc::AddQuad(root_pass, quad_rect_1, SkColorSetARGB(0x33, 0xFF, 0, 0));
 
   cc::AddQuad(root_pass, quad_rect_2, SK_ColorBLUE);
   root_pass->shared_quad_state_list.back()->opacity = 0.5f;
@@ -3421,14 +3420,13 @@
       gfx::Transform(), cc::FilterOperations());
   root_pass->damage_rect = root_pass_damage_rect;
 
-  cc::AddQuad(root_pass, quad_rect_1, SK_ColorRED);
-  root_pass->quad_list.back()->needs_blending = true;
+  cc::AddQuad(root_pass, quad_rect_1, SkColorSetARGB(0x33, 0xFF, 0, 0));
 
   cc::AddQuad(root_pass, quad_rect_2, SK_ColorBLUE);
   root_pass->shared_quad_state_list.back()->opacity = 0.5f;
 
   cc::AddQuad(root_pass, quad_rect_3, SK_ColorGREEN);
-  root_pass->shared_quad_state_list.back()->blend_mode = SkBlendMode::kDstIn;
+  root_pass->shared_quad_state_list.back()->blend_mode = SkBlendMode::kSrc;
 
   auto* gl = gl_ptr();
 
@@ -3463,7 +3461,8 @@
   // Fast path draw used for red quad.
   EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST));
   EXPECT_CALL(*gl, Scissor(0, 480, 20, 20));
-  EXPECT_CALL(*gl, ClearColor(1, 0, 0, 1));
+  EXPECT_CALL(*gl, ClearColor(::testing::FloatEq(0.2f), 0, 0,
+                              ::testing::FloatEq(0.2f)));
   EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST));
   EXPECT_CALL(*gl, Scissor(0, 0, 0, 0));
 
diff --git a/components/viz/service/display/renderer_pixeltest.cc b/components/viz/service/display/renderer_pixeltest.cc
index dd34126..45762dd 100644
--- a/components/viz/service/display/renderer_pixeltest.cc
+++ b/components/viz/service/display/renderer_pixeltest.cc
@@ -26,6 +26,7 @@
 #include "cc/test/resource_provider_test_utils.h"
 #include "cc/test/test_types.h"
 #include "components/viz/client/client_resource_provider.h"
+#include "components/viz/common/features.h"
 #include "components/viz/common/quads/picture_draw_quad.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
 #include "components/viz/common/resources/bitmap_allocation.h"
@@ -45,6 +46,7 @@
 #include "media/base/video_frame.h"
 #include "media/renderers/video_resource_updater.h"
 #include "media/video/half_float_maker.h"
+#include "third_party/skia/include/core/SkColor.h"
 #include "third_party/skia/include/core/SkColorPriv.h"
 #include "third_party/skia/include/core/SkMatrix.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
@@ -3080,6 +3082,90 @@
       cc::FuzzyPixelOffByOneComparator(false)));
 }
 
+// Tests if drawing using the fast solid color draw feature returns the same
+// results as drawing without the feature.
+class GLRendererPixelTestFastSolidColorDraw
+    : public VizPixelTest,
+      public testing::WithParamInterface<SkBlendMode> {
+ public:
+  GLRendererPixelTestFastSolidColorDraw() : VizPixelTest(RendererType::kGL) {}
+  void SetUp() override {
+    feature_list_.InitAndEnableFeature(features::kFastSolidColorDraw);
+    VizPixelTest::SetUp();
+  }
+
+ protected:
+  void SetUpRenderPassList() {
+    // Sets up a root render pass with three solid color draw quads.
+    // As the render pass has transparent background, the fast path is used to
+    // draw the quad on the left (semi-transparent) and the quad in the center
+    // (fully-opaque) when the blend mode is kSrcOver, but not when kScreen,
+    // kDstIn or kDstOut.
+    // The quad on the right is fully opaque and has the default kSrcOver blend
+    // mode, so it is drawn using the fast path.
+    pass_list_.clear();
+    gfx::Rect device_viewport_rect(this->device_viewport_size_);
+
+    AggregatedRenderPassId root_id{1};
+    auto root_pass = CreateTestRootRenderPass(root_id, device_viewport_rect);
+
+    const int kGridWidth = device_viewport_rect.width() / 3;
+    const int kGridHeight = device_viewport_rect.height() / 3;
+    gfx::Rect left_rect = gfx::Rect(0, kGridHeight, kGridWidth, kGridHeight);
+
+    gfx::Transform identity_quad_to_target_transform;
+    SharedQuadState* shared_state =
+        CreateTestSharedQuadState(identity_quad_to_target_transform, left_rect,
+                                  root_pass.get(), gfx::RRectF());
+    shared_state->blend_mode = GetParam();
+    auto* color_quad = root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
+    color_quad->SetNew(shared_state, left_rect, left_rect,
+                       SkColorSetARGB(0x33, 0xFF, 0, 0), true);
+
+    gfx::Rect center_rect =
+        gfx::Rect(kGridWidth, kGridHeight, kGridWidth, kGridHeight);
+
+    shared_state =
+        CreateTestSharedQuadState(identity_quad_to_target_transform,
+                                  center_rect, root_pass.get(), gfx::RRectF());
+    shared_state->blend_mode = GetParam();
+    color_quad = root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
+    color_quad->SetNew(shared_state, center_rect, center_rect, SK_ColorRED,
+                       true);
+    shared_state->blend_mode = GetParam();
+
+    gfx::Rect right_rect =
+        gfx::Rect(kGridWidth * 2, kGridHeight, kGridWidth, kGridHeight);
+    shared_state =
+        CreateTestSharedQuadState(identity_quad_to_target_transform, right_rect,
+                                  root_pass.get(), gfx::RRectF());
+    color_quad = root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
+    color_quad->SetNew(shared_state, right_rect, right_rect, SK_ColorRED, true);
+
+    pass_list_.push_back(std::move(root_pass));
+  }
+  AggregatedRenderPassList pass_list_;
+  base::test::ScopedFeatureList feature_list_;
+};
+
+TEST_P(GLRendererPixelTestFastSolidColorDraw, ResultSameAsFeatureDisabled) {
+  this->SetUpRenderPassList();
+  char buff[64];
+  memset(buff, 0, 64);
+  snprintf(buff, sizeof(buff), "gl_solid_color_%s.png",
+           SkBlendMode_Name(GetParam()));
+  EXPECT_TRUE(this->RunPixelTest(&this->pass_list_,
+                                 base::FilePath::FromUTF8Unsafe(buff),
+                                 cc::FuzzyPixelOffByOneComparator(true)));
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+                         GLRendererPixelTestFastSolidColorDraw,
+                         testing::Values(SkBlendMode::kSrcOver,
+                                         SkBlendMode::kDstIn,
+                                         SkBlendMode::kDstOut,
+                                         SkBlendMode::kScreen));
+
 class GLRendererPixelTestWithBackdropFilter : public VizPixelTest {
  public:
   GLRendererPixelTestWithBackdropFilter() : VizPixelTest(RendererType::kGL) {}
@@ -3184,6 +3270,7 @@
       &this->pass_list_,
       base::FilePath(FILE_PATH_LITERAL("gl_backdrop_filter_1.png")),
       cc::FuzzyPixelOffByOneComparator(true)));
+
   if (this->context_provider()->ContextCapabilities().major_version < 3)
     return;
   this->backdrop_filter_quality_ = 0.33f;
diff --git a/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc b/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc
index 3ee73ec..725fd78 100644
--- a/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc
+++ b/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc
@@ -186,7 +186,8 @@
                    bool thread_safe,
                    gfx::GpuMemoryBufferType gmb_type,
                    gpu::GrContextType gr_context_type_,
-                   bool* allow_legacy_mailbox) override {
+                   bool* allow_legacy_mailbox,
+                   bool is_pixel_used) override {
     return true;
   }
 };
diff --git a/components/viz/service/gl/info_collection_gpu_service_impl.cc b/components/viz/service/gl/info_collection_gpu_service_impl.cc
index 2fb05a2a..945de99 100644
--- a/components/viz/service/gl/info_collection_gpu_service_impl.cc
+++ b/components/viz/service/gl/info_collection_gpu_service_impl.cc
@@ -61,10 +61,14 @@
         GetGpuSupportedDx12VersionAndDevicePerfInfoCallback callback) {
   DCHECK(main_runner_->BelongsToCurrentThread());
 
-  uint32_t d3d12_feature_level = gpu::GetGpuSupportedD3D12Version();
-  io_runner_->PostTask(FROM_HERE,
-                       base::BindOnce(std::move(callback), d3d12_feature_level,
-                                      device_perf_info_));
+  uint32_t d3d12_feature_level = 0;
+  uint32_t highest_shader_model_version = 0;
+  gpu::GetGpuSupportedD3D12Version(d3d12_feature_level,
+                                   highest_shader_model_version);
+  io_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(callback), d3d12_feature_level,
+                     highest_shader_model_version, device_perf_info_));
 }
 
 void InfoCollectionGpuServiceImpl::GetGpuSupportedVulkanVersionInfo(
diff --git a/components/viz/test/data/gl_solid_color_DstIn.png b/components/viz/test/data/gl_solid_color_DstIn.png
new file mode 100644
index 0000000..35ef6a0
--- /dev/null
+++ b/components/viz/test/data/gl_solid_color_DstIn.png
Binary files differ
diff --git a/components/viz/test/data/gl_solid_color_DstOut.png b/components/viz/test/data/gl_solid_color_DstOut.png
new file mode 100644
index 0000000..35ef6a0
--- /dev/null
+++ b/components/viz/test/data/gl_solid_color_DstOut.png
Binary files differ
diff --git a/components/viz/test/data/gl_solid_color_Screen.png b/components/viz/test/data/gl_solid_color_Screen.png
new file mode 100644
index 0000000..fc9630c8
--- /dev/null
+++ b/components/viz/test/data/gl_solid_color_Screen.png
Binary files differ
diff --git a/components/viz/test/data/gl_solid_color_SrcOver.png b/components/viz/test/data/gl_solid_color_SrcOver.png
new file mode 100644
index 0000000..fc9630c8
--- /dev/null
+++ b/components/viz/test/data/gl_solid_color_SrcOver.png
Binary files differ
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 58f03d3..03d612b 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -270,8 +270,8 @@
   }
 
   public_deps = [
+    "//base",
     "//base/util/memory_pressure",
-    "//base/util/type_safety",
     "//ipc",
     "//media/mojo/mojom:remoting",
     "//third_party/blink/public/mojom:embedded_frame_sink_mojo_bindings",
diff --git a/content/browser/accessibility/accessibility_win_browsertest.cc b/content/browser/accessibility/accessibility_win_browsertest.cc
index fbf7fab3..be3984b7 100644
--- a/content/browser/accessibility/accessibility_win_browsertest.cc
+++ b/content/browser/accessibility/accessibility_win_browsertest.cc
@@ -5169,7 +5169,19 @@
   destroyed_watcher.Wait();
 }
 
-IN_PROC_BROWSER_TEST_F(AccessibilityWinUIABrowserTest,
+class AccessibilityWinUIASelectivelyEnabledBrowserTest
+    : public AccessibilityWinUIABrowserTest {
+ protected:
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kSelectiveUIAEnablement);
+
+    AccessibilityWinUIABrowserTest::SetUp();
+  }
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(AccessibilityWinUIASelectivelyEnabledBrowserTest,
                        RequestingTopLevelElementEnablesWebAccessibility) {
   EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
 
@@ -5190,10 +5202,43 @@
   uia->ElementFromHandle(hwnd, &root);
   ASSERT_NE(nullptr, root.Get());
 
+  // Native API support should now be enabled.
+  ui::AXMode expected_mode = ui::AXMode::kNativeAPIs;
+  EXPECT_EQ(expected_mode, content::BrowserAccessibilityStateImpl::GetInstance()
+                               ->GetAccessibilityMode());
+
+  // Now get the fragment root's first (only) child.
+  Microsoft::WRL::ComPtr<IUIAutomationTreeWalker> tree_walker;
+  uia->get_RawViewWalker(&tree_walker);
+  Microsoft::WRL::ComPtr<IUIAutomationElement> first_child;
+  tree_walker->GetFirstChildElement(root.Get(), &first_child);
+  ASSERT_NE(nullptr, first_child.Get());
+
+  base::win::ScopedBstr name;
+  ASSERT_HRESULT_SUCCEEDED(first_child->get_CurrentName(name.Receive()));
+
   // Web content accessibility support should now be enabled.
-  EXPECT_EQ(ui::kAXModeComplete,
-            content::BrowserAccessibilityStateImpl::GetInstance()
-                ->GetAccessibilityMode());
+  expected_mode |= ui::AXMode::kWebContents;
+  EXPECT_EQ(expected_mode, content::BrowserAccessibilityStateImpl::GetInstance()
+                               ->GetAccessibilityMode());
+
+  Microsoft::WRL::ComPtr<IUnknown> text_pattern_unknown;
+  ASSERT_HRESULT_SUCCEEDED(
+      first_child->GetCurrentPattern(UIA_TextPatternId, &text_pattern_unknown));
+  EXPECT_EQ(nullptr, text_pattern_unknown.Get());
+
+  // Now check that inline text box support is enabled as well.
+  expected_mode |= ui::AXMode::kInlineTextBoxes;
+  EXPECT_EQ(expected_mode, content::BrowserAccessibilityStateImpl::GetInstance()
+                               ->GetAccessibilityMode());
+
+  Microsoft::WRL::ComPtr<IUIAutomationElement> labelled_by;
+  ASSERT_HRESULT_SUCCEEDED(first_child->get_CurrentLabeledBy(&labelled_by));
+
+  // Now check that we have complete accessibility support enabled.
+  expected_mode |= ui::AXMode::kScreenReader;
+  EXPECT_EQ(expected_mode, content::BrowserAccessibilityStateImpl::GetInstance()
+                               ->GetAccessibilityMode());
 }
 
 IN_PROC_BROWSER_TEST_F(AccessibilityWinUIABrowserTest,
diff --git a/content/browser/accessibility/browser_accessibility_state_impl_win.cc b/content/browser/accessibility/browser_accessibility_state_impl_win.cc
index 9c1ea74..a15418f 100644
--- a/content/browser/accessibility/browser_accessibility_state_impl_win.cc
+++ b/content/browser/accessibility/browser_accessibility_state_impl_win.cc
@@ -74,18 +74,38 @@
     BrowserAccessibilityStateImpl::GetInstance()->OnAccessibilityApiUsage();
   }
 
-  void OnUIAutomationUsed() override {
+  void OnBasicUIAutomationUsed() override {
+    AddAXModeForUIA(ui::AXMode::kNativeAPIs);
+  }
+
+  void OnAdvancedUIAutomationUsed() override {
+    AddAXModeForUIA(ui::AXMode::kWebContents);
+  }
+
+  void OnProbableUIAutomationScreenReaderDetected() override {
+    // Same as kAXModeComplete but without kHTML as it is not needed for UIA.
+    AddAXModeForUIA(ui::kAXModeCompleteNoHTML);
+  }
+
+  void OnTextPatternRequested() override {
+    AddAXModeForUIA(ui::AXMode::kInlineTextBoxes);
+  }
+
+  void AddAXModeForUIA(ui::AXMode mode) {
     DCHECK(::switches::IsExperimentalAccessibilityPlatformUIAEnabled());
 
     // Firing a UIA event can cause UIA to call back into our APIs, don't
     // consider this to be usage.
     if (firing_uia_events_)
       return;
+
     // UI Automation insulates providers from knowing about the client(s) asking
-    // for information. When UI Automation is requested, assume the presence of
-    // a full-fledged accessibility technology and enable full support.
+    // for information. When IsSelectiveUIAEnablement is Enabled, we turn on
+    // various parts of accessibility depending on what APIs have been called.
+    if (!features::IsSelectiveUIAEnablementEnabled())
+      mode = ui::kAXModeComplete;
     BrowserAccessibilityStateImpl::GetInstance()->AddAccessibilityModeFlags(
-        ui::kAXModeComplete);
+        mode);
     BrowserAccessibilityStateImpl::GetInstance()->OnAccessibilityApiUsage();
   }
 
diff --git a/content/browser/accessibility/snapshot_ax_tree_browsertest.cc b/content/browser/accessibility/snapshot_ax_tree_browsertest.cc
index fd76334a..c4cbcd9 100644
--- a/content/browser/accessibility/snapshot_ax_tree_browsertest.cc
+++ b/content/browser/accessibility/snapshot_ax_tree_browsertest.cc
@@ -552,11 +552,12 @@
       /* timeout= */ {});
   waiter.Wait();
 
-  EXPECT_THAT(waiter.snapshot().tree_data.metadata,
-              testing::ElementsAre(
-                  "<title>Hello World</title>", "<meta charset=utf-8></meta>",
-                  "<link ref=canonical href=https://abc.com></link>",
-                  "<script type=application/ld+json>{}</script>"));
+  EXPECT_THAT(
+      waiter.snapshot().tree_data.metadata,
+      testing::ElementsAre(
+          "<title>Hello World</title>", "<meta charset=\"utf-8\"></meta>",
+          "<link ref=\"canonical\" href=\"https://abc.com\"></link>",
+          "<script type=\"application/ld+json\">{}</script>"));
 }
 
 }  // namespace content
diff --git a/content/browser/conversions/conversion_storage_sql.cc b/content/browser/conversions/conversion_storage_sql.cc
index eb28743..b9c2a1e 100644
--- a/content/browser/conversions/conversion_storage_sql.cc
+++ b/content/browser/conversions/conversion_storage_sql.cc
@@ -12,6 +12,7 @@
 #include "base/bind.h"
 #include "base/check_op.h"
 #include "base/compiler_specific.h"
+#include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
@@ -329,12 +330,7 @@
   }
 
   // Otherwise, delete the existing report with the lowest priority.
-  static constexpr char kDeleteConversionSql[] =
-      "DELETE FROM conversions WHERE conversion_id = ?";
-  sql::Statement delete_statement(
-      db_->GetCachedStatement(SQL_FROM_HERE, kDeleteConversionSql));
-  delete_statement.BindInt64(0, conversion_id_with_min_priority);
-  return delete_statement.Run()
+  return DeleteConversionInternal(conversion_id_with_min_priority)
              ? ConversionStorageSql::MaybeReplaceLowerPriorityReportResult::
                    kReplaceOldReport
              : ConversionStorageSql::MaybeReplaceLowerPriorityReportResult::
@@ -506,23 +502,15 @@
   }
 
   // Delete all unattributed impressions.
-  static constexpr char kDeleteUnattributedImpressionsSql[] =
-      "DELETE FROM impressions WHERE impression_id = ?";
-  sql::Statement delete_impression_statement(db_->GetCachedStatement(
-      SQL_FROM_HERE, kDeleteUnattributedImpressionsSql));
+  if (!DeleteImpressions(impression_ids_to_delete))
+    return false;
 
-  for (int64_t impression_id : impression_ids_to_delete) {
-    delete_impression_statement.Reset(/*clear_bound_vars=*/true);
-    delete_impression_statement.BindInt64(0, impression_id);
-    if (!delete_impression_statement.Run())
-      return false;
-    // Based on the deletion logic here and the fact that we delete impressions
-    // with |num_conversions > 1| when there is a new matching impression in
-    // |StoreImpression()|, we should be guaranteed that these impressions all
-    // have |num_conversions == 0|, and that they never contributed to a rate
-    // limit. Therefore, we don't need to call
-    // |RateLimitTable::ClearDataForImpressionIds()| here.
-  }
+  // Based on the deletion logic here and the fact that we delete impressions
+  // with |num_conversions > 1| when there is a new matching impression in
+  // |StoreImpression()|, we should be guaranteed that these impressions all
+  // have |num_conversions == 0|, and that they never contributed to a rate
+  // limit. Therefore, we don't need to call
+  // |RateLimitTable::ClearDataForImpressionIds()| here.
 
   if (create_report && !rate_limit_table_.AddRateLimit(db_.get(), report))
     return false;
@@ -661,18 +649,19 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!LazyInit(DbCreationPolicy::kIgnoreIfAbsent))
     return false;
+  if (!DeleteConversionInternal(conversion_id))
+    return false;
+  return db_->GetLastChangeCount() > 0;
+}
 
+bool ConversionStorageSql::DeleteConversionInternal(int64_t conversion_id) {
   // Delete the row identified by |conversion_id|.
   static constexpr char kDeleteSentConversionSql[] =
       "DELETE FROM conversions WHERE conversion_id = ?";
   sql::Statement statement(
       db_->GetCachedStatement(SQL_FROM_HERE, kDeleteSentConversionSql));
   statement.BindInt64(0, conversion_id);
-
-  if (!statement.Run())
-    return false;
-
-  return db_->GetLastChangeCount() > 0;
+  return statement.Run();
 }
 
 void ConversionStorageSql::ClearData(
@@ -723,18 +712,17 @@
     }
   }
 
-  // Since multiple conversions can be associated with a single impression,
-  // |impression_ids_to_delete| may contain duplicates. Remove duplicates by
-  // converting the vector into a flat_set. Internally, this sorts the vector
-  // and then removes duplicates.
-  const base::flat_set<int64_t> unique_impression_ids_to_delete(
-      impression_ids_to_delete);
-
   // TODO(csharrison, johnidel): Should we consider poisoning the DB if some of
   // the delete operations fail?
   if (!statement.Succeeded())
     return;
 
+  // Since multiple conversions can be associated with a single impression,
+  // deduplicate impression IDs using a set to avoid redundant DB operations
+  // below.
+  impression_ids_to_delete =
+      base::flat_set<int64_t>(std::move(impression_ids_to_delete)).extract();
+
   // Delete the data in a transaction to avoid cases where the impression part
   // of a conversion is deleted without deleting the associated conversion, or
   // vice versa.
@@ -742,23 +730,11 @@
   if (!transaction.Begin())
     return;
 
-  for (int64_t impression_id : unique_impression_ids_to_delete) {
-    static constexpr char kDeleteImpressionSql[] =
-        "DELETE FROM impressions WHERE impression_id = ?";
-    sql::Statement impression_statement(
-        db_->GetCachedStatement(SQL_FROM_HERE, kDeleteImpressionSql));
-    impression_statement.BindInt64(0, impression_id);
-    if (!impression_statement.Run())
-      return;
-  }
+  if (!DeleteImpressions(impression_ids_to_delete))
+    return;
 
   for (int64_t conversion_id : conversion_ids_to_delete) {
-    static constexpr char kDeleteConversionSql[] =
-        "DELETE FROM conversions WHERE conversion_id = ?";
-    sql::Statement conversion_statement(
-        db_->GetCachedStatement(SQL_FROM_HERE, kDeleteConversionSql));
-    conversion_statement.BindInt64(0, conversion_id);
-    if (!conversion_statement.Run())
+    if (!DeleteConversionInternal(conversion_id))
       return;
   }
 
@@ -771,7 +747,7 @@
   // second conversion in limbo (it was not in the deletion time range).
   // Delete all unattributed conversions here to ensure everything is cleaned
   // up.
-  for (int64_t impression_id : unique_impression_ids_to_delete) {
+  for (int64_t impression_id : impression_ids_to_delete) {
     static constexpr char kDeleteVestigialConversionSql[] =
         "DELETE FROM conversions WHERE impression_id = ?";
     sql::Statement delete_vestigial_statement(
@@ -783,19 +759,20 @@
     num_conversions_deleted += db_->GetLastChangeCount();
   }
 
-  if (!rate_limit_table_.ClearDataForImpressionIds(
-          db_.get(), unique_impression_ids_to_delete))
+  if (!rate_limit_table_.ClearDataForImpressionIds(db_.get(),
+                                                   impression_ids_to_delete)) {
     return;
+  }
 
   if (!rate_limit_table_.ClearDataForOriginsInRange(db_.get(), delete_begin,
-                                                    delete_end, filter))
+                                                    delete_end, filter)) {
     return;
+  }
 
   if (!transaction.Commit())
     return;
 
-  RecordImpressionsDeleted(
-      static_cast<int>(unique_impression_ids_to_delete.size()));
+  RecordImpressionsDeleted(static_cast<int>(impression_ids_to_delete.size()));
   RecordReportsDeleted(num_conversions_deleted);
 }
 
@@ -832,24 +809,16 @@
   select_impressions_statement.BindTime(0, delete_begin);
   select_impressions_statement.BindTime(1, delete_end);
 
-  base::flat_set<int64_t> impression_ids_to_delete;
+  std::vector<int64_t> impression_ids_to_delete;
   while (select_impressions_statement.Step()) {
     int64_t impression_id = select_impressions_statement.ColumnInt64(0);
-    impression_ids_to_delete.insert(impression_id);
+    impression_ids_to_delete.push_back(impression_id);
   }
   if (!select_impressions_statement.Succeeded())
     return;
 
-  static constexpr char kDeleteImpressionSql[] =
-      "DELETE FROM impressions WHERE impression_id = ?";
-  sql::Statement delete_impression_statement(
-      db_->GetCachedStatement(SQL_FROM_HERE, kDeleteImpressionSql));
-  for (int64_t impression_id : impression_ids_to_delete) {
-    delete_impression_statement.Reset(/*clear_bound_vars=*/true);
-    delete_impression_statement.BindInt64(0, impression_id);
-    if (!delete_impression_statement.Run())
-      return;
-  }
+  if (!DeleteImpressions(impression_ids_to_delete))
+    return;
 
   static constexpr char kDeleteConversionRangeSql[] =
       "DELETE FROM conversions WHERE(conversion_time BETWEEN ? AND ?)"
@@ -1068,6 +1037,7 @@
   RecordInitializationStatus(InitStatus::kSuccess);
   return true;
 }
+
 bool ConversionStorageSql::InitializeSchema(bool db_empty) {
   if (db_empty)
     return CreateSchema();
@@ -1347,6 +1317,15 @@
       break;
   }
 
+  return DeleteImpressions(impression_ids_to_delete);
+
+  // Because this is limited to active impressions with `num_conversions = 0`,
+  // we should be guaranteed that there is not any corresponding data in the
+  // rate limit table or the report table.
+}
+
+bool ConversionStorageSql::DeleteImpressions(
+    const std::vector<int64_t>& impression_ids) {
   sql::Transaction transaction(db_.get());
   if (!transaction.Begin())
     return false;
@@ -1356,17 +1335,13 @@
   sql::Statement delete_impression_statement(
       db_->GetCachedStatement(SQL_FROM_HERE, kDeleteImpressionSql));
 
-  for (int64_t impression_id : impression_ids_to_delete) {
+  for (int64_t impression_id : impression_ids) {
     delete_impression_statement.Reset(/*clear_bound_vars=*/true);
     delete_impression_statement.BindInt64(0, impression_id);
     if (!delete_impression_statement.Run())
       return false;
   }
 
-  // Because this is limited to active impressions with `num_conversions = 0`,
-  // we should be guaranteed that there is not any corresponding data in the
-  // rate limit table or the report table.
-
   return transaction.Commit();
 }
 
diff --git a/content/browser/conversions/conversion_storage_sql.h b/content/browser/conversions/conversion_storage_sql.h
index 34ad0c6..57d6bc7 100644
--- a/content/browser/conversions/conversion_storage_sql.h
+++ b/content/browser/conversions/conversion_storage_sql.h
@@ -95,6 +95,15 @@
       VALID_CONTEXT_REQUIRED(sequence_checker_);
   void ClearAllDataAllTime() VALID_CONTEXT_REQUIRED(sequence_checker_);
 
+  // Returns false on failure.
+  bool DeleteImpressions(const std::vector<int64_t>& impression_ids)
+      VALID_CONTEXT_REQUIRED(sequence_checker_) WARN_UNUSED_RESULT;
+  // Deletes the conversion with `conversion_id` without checking the the DB
+  // initialization status or the number of deleted rows. Returns false on
+  // failure.
+  bool DeleteConversionInternal(int64_t conversion_id)
+      VALID_CONTEXT_REQUIRED(sequence_checker_) WARN_UNUSED_RESULT;
+
   bool HasCapacityForStoringImpression(const std::string& serialized_origin)
       VALID_CONTEXT_REQUIRED(sequence_checker_) WARN_UNUSED_RESULT;
   int GetCapacityForStoringConversion(const std::string& serialized_origin)
diff --git a/content/browser/conversions/rate_limit_table.cc b/content/browser/conversions/rate_limit_table.cc
index 099a1496..dc93a605 100644
--- a/content/browser/conversions/rate_limit_table.cc
+++ b/content/browser/conversions/rate_limit_table.cc
@@ -226,18 +226,20 @@
 
 bool RateLimitTable::ClearDataForImpressionIds(
     sql::Database* db,
-    const base::flat_set<int64_t>& impression_ids) {
+    const std::vector<int64_t>& impression_ids) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   sql::Transaction transaction(db);
   if (!transaction.Begin())
     return false;
 
+  static constexpr char kDeleteRateLimitSql[] =
+      "DELETE FROM rate_limits WHERE impression_id = ?";
+  sql::Statement statement(
+      db->GetCachedStatement(SQL_FROM_HERE, kDeleteRateLimitSql));
+
   for (int64_t id : impression_ids) {
-    static constexpr char kDeleteRateLimitSql[] =
-        "DELETE FROM rate_limits WHERE impression_id = ?";
-    sql::Statement statement(
-        db->GetCachedStatement(SQL_FROM_HERE, kDeleteRateLimitSql));
+    statement.Reset(/*clear_bound_vars=*/true);
     statement.BindInt64(0, id);
     if (!statement.Run())
       return false;
diff --git a/content/browser/conversions/rate_limit_table.h b/content/browser/conversions/rate_limit_table.h
index c3fccc9d..630ecc5f 100644
--- a/content/browser/conversions/rate_limit_table.h
+++ b/content/browser/conversions/rate_limit_table.h
@@ -5,9 +5,10 @@
 #ifndef CONTENT_BROWSER_CONVERSIONS_RATE_LIMIT_TABLE_H_
 #define CONTENT_BROWSER_CONVERSIONS_RATE_LIMIT_TABLE_H_
 
+#include <vector>
+
 #include "base/callback.h"
 #include "base/compiler_specific.h"
-#include "base/containers/flat_set.h"
 #include "base/sequence_checker.h"
 #include "base/thread_annotations.h"
 #include "base/time/time.h"
@@ -67,7 +68,7 @@
       base::RepeatingCallback<bool(const url::Origin&)> filter)
       WARN_UNUSED_RESULT;
   bool ClearDataForImpressionIds(sql::Database* db,
-                                 const base::flat_set<int64_t>& impression_ids)
+                                 const std::vector<int64_t>& impression_ids)
       WARN_UNUSED_RESULT;
 
  private:
diff --git a/content/browser/cookie_store/cookie_change_subscription.cc b/content/browser/cookie_store/cookie_change_subscription.cc
index 07b9b55..61dfa9ab 100644
--- a/content/browser/cookie_store/cookie_change_subscription.cc
+++ b/content/browser/cookie_store/cookie_change_subscription.cc
@@ -9,6 +9,7 @@
 #include "content/browser/cookie_store/cookie_change_subscriptions.pb.h"
 #include "net/cookies/cookie_constants.h"
 #include "net/cookies/cookie_util.h"
+#include "net/cookies/same_party_context.h"
 #include "services/network/public/cpp/is_potentially_trustworthy.h"
 
 namespace content {
@@ -171,8 +172,7 @@
   net::CookieOptions net_options;
   net_options.set_same_site_cookie_context(
       net::CookieOptions::SameSiteCookieContext::MakeInclusive());
-  net_options.set_same_party_cookie_context_type(
-      net::CookieOptions::SamePartyCookieContextType::kSameParty);
+  net_options.set_same_party_context(net::SamePartyContext::MakeInclusive());
   // It doesn't matter which we choose here, since both SameParty and SameSite
   // semantics should allow this access. But we make a choice to be explicit.
   net_options.set_is_in_nontrivial_first_party_set(true);
diff --git a/content/browser/download/save_types.h b/content/browser/download/save_types.h
index 219aa86..7cec55ee 100644
--- a/content/browser/download/save_types.h
+++ b/content/browser/download/save_types.h
@@ -13,16 +13,16 @@
 #include <vector>
 
 #include "base/files/file_path.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "url/gurl.h"
 
 namespace content {
 
 class SavePackage;
-using SavePackageId = util::IdType32<SavePackage>;
+using SavePackageId = base::IdType32<SavePackage>;
 
 class SaveItem;
-using SaveItemId = util::IdType32<SaveItem>;
+using SaveItemId = base::IdType32<SaveItem>;
 
 // Map from save_item_id into final file path.
 using FinalNamesMap =
diff --git a/content/browser/gpu/gpu_data_manager_impl_private.cc b/content/browser/gpu/gpu_data_manager_impl_private.cc
index d0913d3..b7ad87a 100644
--- a/content/browser/gpu/gpu_data_manager_impl_private.cc
+++ b/content/browser/gpu/gpu_data_manager_impl_private.cc
@@ -731,6 +731,7 @@
         host->info_collection_gpu_service()
             ->GetGpuSupportedDx12VersionAndDevicePerfInfo(
                 base::BindOnce([](uint32_t d3d12_feature_level,
+                                  uint32_t highest_shader_model_version,
                                   const gpu::DevicePerfInfo& device_perf_info) {
                   GpuDataManagerImpl* manager =
                       GpuDataManagerImpl::GetInstance();
@@ -741,7 +742,7 @@
                   manager->UpdateDevicePerfInfo(device_perf_info);
                   manager->TerminateInfoCollectionGpuProcess();
                   gpu::RecordGpuSupportedDx12VersionHistograms(
-                      d3d12_feature_level);
+                      d3d12_feature_level, highest_shader_model_version);
                 }));
       },
       delta);
diff --git a/content/browser/isolation_context.h b/content/browser/isolation_context.h
index 3551350..2da2801 100644
--- a/content/browser/isolation_context.h
+++ b/content/browser/isolation_context.h
@@ -5,7 +5,7 @@
 #ifndef CONTENT_BROWSER_ISOLATION_CONTEXT_H_
 #define CONTENT_BROWSER_ISOLATION_CONTEXT_H_
 
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/browser_or_resource_context.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -13,7 +13,7 @@
 namespace content {
 
 class BrowsingInstance;
-using BrowsingInstanceId = util::IdType32<BrowsingInstance>;
+using BrowsingInstanceId = base::IdType32<BrowsingInstance>;
 
 // This class is used to specify the context in which process model decisions
 // need to be made.  For example, dynamically added isolated origins only take
diff --git a/content/browser/renderer_host/cookie_utils.cc b/content/browser/renderer_host/cookie_utils.cc
index b686284..ebdc561 100644
--- a/content/browser/renderer_host/cookie_utils.cc
+++ b/content/browser/renderer_host/cookie_utils.cc
@@ -91,6 +91,12 @@
   bool same_party_exclusion_overruled_samesite = false;
   bool same_party_inclusion_overruled_samesite = false;
 
+  bool samesite_none_cookie_required = false;
+  bool samesite_none_cookie_sameparty_included_by_top_resource = false;
+  bool samesite_none_cookie_sameparty_included_by_ancestors = false;
+  bool samesite_none_cookie_included_by_samesite_lax = false;
+  bool samesite_none_cookie_included_by_samesite_strict = false;
+
   for (const network::mojom::CookieOrLineWithAccessResultPtr& cookie :
        cookie_details->cookie_list) {
     if (ShouldReportDevToolsIssueForStatus(cookie->access_result.status)) {
@@ -140,6 +146,31 @@
           status.HasWarningReason(
               net::CookieInclusionStatus::
                   WARN_SAMEPARTY_INCLUSION_OVERRULED_SAMESITE);
+
+      samesite_none_cookie_required =
+          samesite_none_cookie_required ||
+          status.HasWarningReason(
+              net::CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED);
+      samesite_none_cookie_sameparty_included_by_top_resource =
+          samesite_none_cookie_sameparty_included_by_top_resource ||
+          status.HasWarningReason(
+              net::CookieInclusionStatus::
+                  WARN_SAMESITE_NONE_INCLUDED_BY_SAMEPARTY_TOP_RESOURCE);
+      samesite_none_cookie_sameparty_included_by_ancestors =
+          samesite_none_cookie_sameparty_included_by_ancestors ||
+          status.HasWarningReason(
+              net::CookieInclusionStatus::
+                  WARN_SAMESITE_NONE_INCLUDED_BY_SAMEPARTY_ANCESTORS);
+      samesite_none_cookie_included_by_samesite_lax =
+          samesite_none_cookie_included_by_samesite_lax ||
+          status.HasWarningReason(
+              net::CookieInclusionStatus::
+                  WARN_SAMESITE_NONE_INCLUDED_BY_SAMESITE_LAX);
+      samesite_none_cookie_included_by_samesite_strict =
+          samesite_none_cookie_included_by_samesite_strict ||
+          status.HasWarningReason(
+              net::CookieInclusionStatus::
+                  WARN_SAMESITE_NONE_INCLUDED_BY_SAMESITE_STRICT);
     }
 
     breaking_context_downgrade =
@@ -190,6 +221,29 @@
         rfh,
         blink::mojom::WebFeature::kSamePartyCookieInclusionOverruledSameSite);
   }
+
+  if (samesite_none_cookie_required) {
+    GetContentClient()->browser()->LogWebFeatureForCurrentPage(
+        rfh, blink::mojom::WebFeature::kSameSiteNoneRequired);
+  }
+  if (samesite_none_cookie_sameparty_included_by_top_resource) {
+    GetContentClient()->browser()->LogWebFeatureForCurrentPage(
+        rfh,
+        blink::mojom::WebFeature::kSameSiteNoneIncludedBySamePartyTopResource);
+  }
+  if (samesite_none_cookie_sameparty_included_by_ancestors) {
+    GetContentClient()->browser()->LogWebFeatureForCurrentPage(
+        rfh,
+        blink::mojom::WebFeature::kSameSiteNoneIncludedBySamePartyAncestors);
+  }
+  if (samesite_none_cookie_included_by_samesite_lax) {
+    GetContentClient()->browser()->LogWebFeatureForCurrentPage(
+        rfh, blink::mojom::WebFeature::kSameSiteNoneIncludedBySameSiteLax);
+  }
+  if (samesite_none_cookie_included_by_samesite_strict) {
+    GetContentClient()->browser()->LogWebFeatureForCurrentPage(
+        rfh, blink::mojom::WebFeature::kSameSiteNoneIncludedBySameSiteStrict);
+  }
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/frame_tree.h b/content/browser/renderer_host/frame_tree.h
index 674aa48a..ec42d95 100644
--- a/content/browser/renderer_host/frame_tree.h
+++ b/content/browser/renderer_host/frame_tree.h
@@ -194,7 +194,7 @@
   }
   PageDelegate* page_delegate() { return page_delegate_; }
 
-  using RenderViewHostMapId = util::IdType32<class RenderViewHostMap>;
+  using RenderViewHostMapId = base::IdType32<class RenderViewHostMap>;
   using RenderViewHostMap = std::unordered_map<RenderViewHostMapId,
                                                RenderViewHostImpl*,
                                                RenderViewHostMapId::Hasher>;
diff --git a/content/browser/ssl/ssl_manager.cc b/content/browser/ssl/ssl_manager.cc
index 2f8ef0bc..762dd03 100644
--- a/content/browser/ssl/ssl_manager.cc
+++ b/content/browser/ssl/ssl_manager.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/utf_string_conversions.h"
@@ -28,6 +29,8 @@
 #include "content/public/browser/navigation_details.h"
 #include "content/public/browser/ssl_host_state_delegate.h"
 #include "content/public/common/content_client.h"
+#include "content/public/common/content_switches.h"
+#include "net/base/url_util.h"
 #include "net/cert/cert_status_flags.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
@@ -310,12 +313,22 @@
 void SSLManager::OnCertError(std::unique_ptr<SSLErrorHandler> handler) {
   // First we check if we know the policy for this error.
   DCHECK(handler->ssl_info().is_valid());
-  SSLHostStateDelegate::CertJudgment judgment =
-      ssl_host_state_delegate_
-          ? ssl_host_state_delegate_->QueryPolicy(
-                handler->request_url().host(), *handler->ssl_info().cert.get(),
-                handler->cert_error(), handler->web_contents())
-          : SSLHostStateDelegate::DENIED;
+
+  SSLHostStateDelegate::CertJudgment judgment;
+  if (net::IsLocalhost(handler->request_url()) &&
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kAllowInsecureLocalhost)) {
+    // If the appropriate flag is set, let requests on localhost go
+    // through even if there are certificate errors. Errors on localhost
+    // are unlikely to indicate actual security problems.
+    judgment = SSLHostStateDelegate::ALLOWED;
+  } else if (ssl_host_state_delegate_) {
+    judgment = ssl_host_state_delegate_->QueryPolicy(
+        handler->request_url().host(), *handler->ssl_info().cert.get(),
+        handler->cert_error(), handler->web_contents());
+  } else {
+    judgment = SSLHostStateDelegate::DENIED;
+  }
 
   if (judgment == SSLHostStateDelegate::ALLOWED) {
     handler->ContinueRequest();
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 7895449..19fa903 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -7338,7 +7338,8 @@
     RenderFrameHostImpl* render_frame_host) {
   OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::DocumentOnLoadCompleted",
                         "render_frame_host", render_frame_host);
-  ShowInsecureLocalhostWarningIfNeeded();
+  DCHECK(render_frame_host->is_main_frame());
+  ShowInsecureLocalhostWarningIfNeeded(render_frame_host->GetPage());
 
   observers_.NotifyObservers(
       &WebContentsObserver::DocumentOnLoadCompletedInMainFrame,
@@ -8749,29 +8750,31 @@
     receiver_sets_.erase(it);
 }
 
-void WebContentsImpl::ShowInsecureLocalhostWarningIfNeeded() {
+void WebContentsImpl::ShowInsecureLocalhostWarningIfNeeded(PageImpl& page) {
   OPTIONAL_TRACE_EVENT0(
       "content", "WebContentsImpl::ShowInsecureLocalhostWarningIfNeeded");
+
   bool allow_localhost = base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kAllowInsecureLocalhost);
   if (!allow_localhost)
     return;
 
-  content::NavigationEntry* entry = GetController().GetLastCommittedEntry();
+  RenderFrameHostImpl& frame = page.GetMainDocument();
+  NavigationEntry* entry =
+      frame.frame_tree()->controller().GetLastCommittedEntry();
   if (!entry || !net::IsLocalhost(entry->GetURL()))
     return;
 
-  content::SSLStatus ssl_status = entry->GetSSL();
+  SSLStatus ssl_status = entry->GetSSL();
   if (!net::IsCertStatusError(ssl_status.cert_status))
     return;
 
-  GetMainFrame()->AddMessageToConsole(
-      blink::mojom::ConsoleMessageLevel::kWarning,
-      base::StringPrintf("This site does not have a valid SSL "
-                         "certificate! Without SSL, your site's and "
-                         "visitors' data is vulnerable to theft and "
-                         "tampering. Get a valid SSL certificate before"
-                         " releasing your website to the public."));
+  frame.AddMessageToConsole(blink::mojom::ConsoleMessageLevel::kWarning,
+                            "This site does not have a valid SSL "
+                            "certificate! Without SSL, your site's and "
+                            "visitors' data is vulnerable to theft and "
+                            "tampering. Get a valid SSL certificate before "
+                            " releasing your website to the public.");
 }
 
 bool WebContentsImpl::IsShowingContextMenuOnPage() const {
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 6ff6b55..ecea2b1 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -1710,7 +1710,7 @@
 
   // Prints a console warning when visiting a localhost site with a bad
   // certificate via --allow-insecure-localhost.
-  void ShowInsecureLocalhostWarningIfNeeded();
+  void ShowInsecureLocalhostWarningIfNeeded(PageImpl& page);
 
   // Format of |headers| is a new line separated list of key value pairs:
   // "<key1>: <value1>\r\n<key2>: <value2>".
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index 3c438ee..55ed7f5 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -68,6 +68,7 @@
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/no_renderer_crashes_assertion.h"
+#include "content/public/test/prerender_test_util.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "content/public/test/url_loader_interceptor.h"
@@ -4864,4 +4865,92 @@
   EXPECT_FALSE(shell()->web_contents()->IsCrashed());
 }
 
+class WebContentsImplInsecureLocalhostBrowserTest
+    : public WebContentsImplBrowserTest {
+ protected:
+  void SetUpOnMainThread() override {
+    WebContentsImplBrowserTest::SetUpOnMainThread();
+    https_server_.AddDefaultHandlers(GetTestDataFilePath());
+  }
+
+  net::EmbeddedTestServer& https_server() { return https_server_; }
+
+ private:
+  net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS};
+};
+
+IN_PROC_BROWSER_TEST_F(WebContentsImplInsecureLocalhostBrowserTest,
+                       BlocksByDefault) {
+  https_server().SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
+  ASSERT_TRUE(https_server().Start());
+  GURL url = https_server().GetURL("/title1.html");
+
+  NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1);
+  EXPECT_TRUE(
+      IsLastCommittedEntryOfPageType(shell()->web_contents(), PAGE_TYPE_ERROR));
+}
+
+class WebContentsImplAllowInsecureLocalhostBrowserTest
+    : public WebContentsImplInsecureLocalhostBrowserTest {
+ protected:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(switches::kAllowInsecureLocalhost);
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(WebContentsImplAllowInsecureLocalhostBrowserTest,
+                       WarnsWithSwitch) {
+  https_server().SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
+  ASSERT_TRUE(https_server().Start());
+  GURL url = https_server().GetURL("/title1.html");
+
+  WebContentsConsoleObserver observer(shell()->web_contents());
+  observer.SetFilter(base::BindRepeating(
+      [](const GURL& expected_url,
+         const WebContentsConsoleObserver::Message& message) {
+        return message.source_frame->GetLastCommittedURL() == expected_url;
+      },
+      url));
+  observer.SetPattern("*SSL certificate*");
+
+  ASSERT_TRUE(NavigateToURL(shell(), url));
+  observer.Wait();
+}
+
+class WebContentsImplAllowInsecureLocalhostPrerenderBrowserTest
+    : public WebContentsImplAllowInsecureLocalhostBrowserTest {
+ protected:
+  test::PrerenderTestHelper& prerender_test_helper() {
+    return prerender_test_helper_;
+  }
+
+ private:
+  test::PrerenderTestHelper prerender_test_helper_{base::BindRepeating(
+      [](decltype(this) test) { return test->shell()->web_contents(); },
+      base::Unretained(this))};
+};
+
+IN_PROC_BROWSER_TEST_F(
+    WebContentsImplAllowInsecureLocalhostPrerenderBrowserTest,
+    WarnsInPrerenderWithSwitch) {
+  ASSERT_TRUE(https_server().Start());
+  ASSERT_TRUE(NavigateToURL(shell(), https_server().GetURL("/title1.html")));
+
+  https_server().ResetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED,
+                                net::SSLServerConfig());
+  GURL prerender_url = https_server().GetURL("/title2.html");
+
+  WebContentsConsoleObserver observer(shell()->web_contents());
+  observer.SetFilter(base::BindRepeating(
+      [](const GURL& expected_url,
+         const WebContentsConsoleObserver::Message& message) {
+        return message.source_frame->GetLastCommittedURL() == expected_url;
+      },
+      prerender_url));
+  observer.SetPattern("*SSL certificate*");
+
+  prerender_test_helper().AddPrerender(prerender_url);
+  observer.Wait();
+}
+
 }  // namespace content
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/AssistViewStructureTest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/AssistViewStructureTest.java
index 1c27053..cdb682e38 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/AssistViewStructureTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/AssistViewStructureTest.java
@@ -279,9 +279,10 @@
         Assert.assertNotNull(metadata);
         Assert.assertEquals(4, metadata.size());
         Assert.assertEquals("<title>Hello World</title>", metadata.get(0));
-        Assert.assertEquals("<meta charset=utf-8></meta>", metadata.get(1));
-        Assert.assertEquals("<link ref=canonical href=https://abc.com></link>", metadata.get(2));
-        Assert.assertEquals("<script type=application/ld+json>{}</script>", metadata.get(3));
+        Assert.assertEquals("<meta charset=\"utf-8\"></meta>", metadata.get(1));
+        Assert.assertEquals(
+                "<link ref=\"canonical\" href=\"https://abc.com\"></link>", metadata.get(2));
+        Assert.assertEquals("<script type=\"application/ld+json\">{}</script>", metadata.get(3));
     }
 
     /**
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityEventsTest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityEventsTest.java
index 832eb9f..e6c63372e 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityEventsTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityEventsTest.java
@@ -208,6 +208,13 @@
 
     @Test
     @SmallTest
+    public void test_addAlertWithRoleChange() {
+        performTest("add-alert-with-role-change.html",
+                "add-alert-with-role-change-expected-android.txt");
+    }
+
+    @Test
+    @SmallTest
     public void test_addChild() {
         performTest("add-child.html", EMPTY_EXPECTATIONS_FILE);
     }
@@ -265,6 +272,12 @@
 
     @Test
     @SmallTest
+    public void test_ariaAtomicChanged2() {
+        performTest("aria-atomic-changed2.html", EMPTY_EXPECTATIONS_FILE);
+    }
+
+    @Test
+    @SmallTest
     public void test_ariaBusyChanged() {
         performTest("aria-busy-changed.html", EMPTY_EXPECTATIONS_FILE);
     }
@@ -466,6 +479,12 @@
 
     @Test
     @SmallTest
+    public void test_ariaRelevantChanged2() {
+        performTest("aria-relevant-changed2.html", EMPTY_EXPECTATIONS_FILE);
+    }
+
+    @Test
+    @SmallTest
     public void test_ariaRequiredChanged() {
         performTest("aria-required-changed.html", EMPTY_EXPECTATIONS_FILE);
     }
@@ -529,6 +548,24 @@
 
     @Test
     @SmallTest
+    public void test_ariaTextboxChildrenChange() {
+        performTest("aria-textbox-children-change.html", EMPTY_EXPECTATIONS_FILE);
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTextboxEditabilityChanges() {
+        performTest("aria-textbox-editability-changes.html", EMPTY_EXPECTATIONS_FILE);
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTextboxWithFocusableChildren() {
+        performTest("aria-textbox-with-focusable-children.html", EMPTY_EXPECTATIONS_FILE);
+    }
+
+    @Test
+    @SmallTest
     public void test_ariaTreeCollapse() {
         performTest("aria-tree-collapse.html", EMPTY_EXPECTATIONS_FILE);
     }
@@ -798,12 +835,24 @@
 
     @Test
     @SmallTest
+    public void test_liveRegionOff() {
+        performTest("live-region-off.html", EMPTY_EXPECTATIONS_FILE);
+    }
+
+    @Test
+    @SmallTest
     public void test_liveRegionRemove() {
         performTest("live-region-remove.html", EMPTY_EXPECTATIONS_FILE);
     }
 
     @Test
     @SmallTest
+    public void test_menuBarShowHideMenus() {
+        performTest("menubar-show-hide-menus.html", EMPTY_EXPECTATIONS_FILE);
+    }
+
+    @Test
+    @SmallTest
     public void test_menulistCollapse() {
         performTest("menulist-collapse.html", EMPTY_EXPECTATIONS_FILE);
     }
@@ -933,6 +982,13 @@
 
     @Test
     @SmallTest
+    public void test_samePageLinkNavigation() {
+        performTest(
+                "same-page-link-navigation.html", "same-page-link-navigation-expected-android.txt");
+    }
+
+    @Test
+    @SmallTest
     @FlakyTest(message = "https://crbug.com/1186376")
     public void test_scrollHorizontalScrollPercentChanged() {
         performTest("scroll-horizontal-scroll-percent-change.html",
diff --git a/content/public/browser/permission_controller.h b/content/public/browser/permission_controller.h
index fe0d9b1..ce7e195 100644
--- a/content/public/browser/permission_controller.h
+++ b/content/public/browser/permission_controller.h
@@ -6,7 +6,7 @@
 #define CONTENT_PUBLIC_BROWSER_PERMISSION_CONTROLLER_H_
 
 #include "base/supports_user_data.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/permission_type.h"
 #include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
@@ -24,7 +24,7 @@
   // Identifier for an active subscription. This is intentionally a distinct
   // type from PermissionControllerDelegate::SubscriptionId as the concrete
   // identifier values may be different.
-  using SubscriptionId = util::IdType64<PermissionController>;
+  using SubscriptionId = base::IdType64<PermissionController>;
 
   ~PermissionController() override {}
 
diff --git a/content/public/browser/permission_controller_delegate.h b/content/public/browser/permission_controller_delegate.h
index 8551bc2..46372b53 100644
--- a/content/public/browser/permission_controller_delegate.h
+++ b/content/public/browser/permission_controller_delegate.h
@@ -5,7 +5,7 @@
 #ifndef CONTENT_PUBLIC_BROWSER_PERMISSION_CONTROLLER_DELEGATE_H_
 #define CONTENT_PUBLIC_BROWSER_PERMISSION_CONTROLLER_DELEGATE_H_
 
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/devtools_permission_overrides.h"
 #include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
@@ -21,7 +21,7 @@
   using PermissionOverrides = DevToolsPermissionOverrides::PermissionOverrides;
 
   // Identifier for an active subscription.
-  using SubscriptionId = util::IdType64<PermissionControllerDelegate>;
+  using SubscriptionId = base::IdType64<PermissionControllerDelegate>;
 
   virtual ~PermissionControllerDelegate() = default;
 
diff --git a/content/public/browser/service_process_info.h b/content/public/browser/service_process_info.h
index cfaab2a..75be1db 100644
--- a/content/public/browser/service_process_info.h
+++ b/content/public/browser/service_process_info.h
@@ -10,7 +10,7 @@
 #include <string>
 
 #include "base/process/process_handle.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "content/common/content_export.h"
 
 namespace content {
@@ -22,7 +22,7 @@
 // An opaque ID type used to uniquely identify service process instances. This
 // is separate from system PID. Values are never reused.
 using ServiceProcessId =
-    util::IdType<internal::ServiceProcessIdTypeMarker, uint64_t, 0u>;
+    base::IdType<internal::ServiceProcessIdTypeMarker, uint64_t, 0u>;
 
 // Information about a running (or very recently running) service process.
 struct CONTENT_EXPORT ServiceProcessInfo {
diff --git a/content/public/browser/ssl_host_state_delegate.h b/content/public/browser/ssl_host_state_delegate.h
index 1e2efe9..e3f07d0 100644
--- a/content/public/browser/ssl_host_state_delegate.h
+++ b/content/public/browser/ssl_host_state_delegate.h
@@ -71,6 +71,14 @@
                                          int child_id,
                                          InsecureContentType content_type) = 0;
 
+  // Allowlists site so it can be loaded over HTTPS when HTTPS-Only Mode is
+  // enabled.
+  virtual void AllowHttpForHost(const std::string& host) = 0;
+
+  // Returns whether site is allowed to load over HTTPS when HTTPS-Only Mode is
+  // enabled.
+  virtual bool IsHttpAllowedForHost(const std::string& host) = 0;
+
   // Revokes all SSL certificate error allow exceptions made by the user for
   // |host|.
   virtual void RevokeUserAllowExceptions(const std::string& host) = 0;
diff --git a/content/public/common/content_switch_dependent_feature_overrides.cc b/content/public/common/content_switch_dependent_feature_overrides.cc
index 47fb1cc..4ffe9b0 100644
--- a/content/public/common/content_switch_dependent_feature_overrides.cc
+++ b/content/public/common/content_switch_dependent_feature_overrides.cc
@@ -68,6 +68,9 @@
        std::cref(features::kEnableNewCanvas2DAPI),
        base::FeatureList::OVERRIDE_ENABLE_FEATURE},
       {switches::kEnableExperimentalWebPlatformFeatures,
+       std::cref(features::kEnableCanvas2DLayers),
+       base::FeatureList::OVERRIDE_ENABLE_FEATURE},
+      {switches::kEnableExperimentalWebPlatformFeatures,
        std::cref(features::kCriticalClientHint),
        base::FeatureList::OVERRIDE_ENABLE_FEATURE},
       {switches::kEnableExperimentalWebPlatformFeatures,
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 1b6f66d..c932180 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -95,6 +95,7 @@
 #include "net/cookies/canonical_cookie.h"
 #include "net/cookies/cookie_access_result.h"
 #include "net/cookies/cookie_util.h"
+#include "net/cookies/same_party_context.h"
 #include "net/filter/gzip_header.h"
 #include "net/filter/gzip_source_stream.h"
 #include "net/filter/mock_source_stream.h"
@@ -1975,7 +1976,7 @@
                const GURL& url,
                const std::string& value,
                net::CookieOptions::SameSiteCookieContext context,
-               net::CookieOptions::SamePartyCookieContextType party_context) {
+               net::SamePartyContext::Type party_context) {
   bool result = false;
   base::RunLoop run_loop;
   mojo::Remote<network::mojom::CookieManager> cookie_manager;
@@ -1989,7 +1990,7 @@
   net::CookieOptions options;
   options.set_include_httponly();
   options.set_same_site_cookie_context(context);
-  options.set_same_party_cookie_context_type(party_context);
+  options.set_same_party_context(net::SamePartyContext(party_context));
   cookie_manager->SetCanonicalCookie(
       *cc.get(), url, options,
       base::BindOnce(
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index 466bbdb8..1f1112bb 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -903,15 +903,15 @@
     const GURL& url);
 
 // Sets a cookie for the given url. Uses inclusive SameSiteCookieContext and
-// SamePartyCookieContextType by default, which get cookies regardless of their
+// SamePartyContext::Type by default, which get cookies regardless of their
 // SameSite and SameParty attributes. Returns true on success.
 bool SetCookie(BrowserContext* browser_context,
                const GURL& url,
                const std::string& value,
                net::CookieOptions::SameSiteCookieContext context =
                    net::CookieOptions::SameSiteCookieContext::MakeInclusive(),
-               net::CookieOptions::SamePartyCookieContextType party_context =
-                   net::CookieOptions::SamePartyCookieContextType::kSameParty);
+               net::SamePartyContext::Type party_context =
+                   net::SamePartyContext::Type::kSameParty);
 
 // Deletes cookies matching the provided filter. Returns the number of cookies
 // that were deleted.
diff --git a/content/renderer/accessibility/blink_ax_tree_source.cc b/content/renderer/accessibility/blink_ax_tree_source.cc
index 766c1fb..1e3da03 100644
--- a/content/renderer/accessibility/blink_ax_tree_source.cc
+++ b/content/renderer/accessibility/blink_ax_tree_source.cc
@@ -375,8 +375,8 @@
       std::string tag = base::ToLowerASCII(elem.TagName().Utf8());
       std::string html = "<" + tag;
       for (unsigned i = 0; i < elem.AttributeCount(); i++) {
-        html += " " + elem.AttributeLocalName(i).Utf8() + "=" +
-                elem.AttributeValue(i).Utf8();
+        html += " " + elem.AttributeLocalName(i).Utf8() + "=\"" +
+                elem.AttributeValue(i).Utf8() + "\"";
       }
       html += ">" + elem.InnerHTML().Utf8() + "</" + tag + ">";
       tree_data->metadata.push_back(html);
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc
index aa0438f..8f42fc3 100644
--- a/content/renderer/accessibility/render_accessibility_impl.cc
+++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -455,7 +455,7 @@
     ax::mojom::Action event_from_action,
     std::vector<ui::AXEventIntent> event_intents) {
   EnqueueDirtyObject(obj, ax::mojom::EventFrom::kAction, event_from_action,
-                     event_intents);
+                     event_intents, dirty_objects_.end());
 
   if (subtree)
     serializer_->InvalidateSubtree(obj);
@@ -627,17 +627,20 @@
   return true;
 }
 
-void RenderAccessibilityImpl::EnqueueDirtyObject(
+std::list<std::unique_ptr<AXDirtyObject>>::iterator
+RenderAccessibilityImpl::EnqueueDirtyObject(
     const blink::WebAXObject& obj,
     ax::mojom::EventFrom event_from,
     ax::mojom::Action event_from_action,
-    std::vector<ui::AXEventIntent> event_intents) {
-  DirtyObject* dirty_object = new DirtyObject();
+    std::vector<ui::AXEventIntent> event_intents,
+    std::list<std::unique_ptr<AXDirtyObject>>::iterator insertion_point) {
+  AXDirtyObject* dirty_object = new AXDirtyObject();
   dirty_object->obj = obj;
   dirty_object->event_from = event_from;
   dirty_object->event_from_action = event_from_action;
   dirty_object->event_intents = event_intents;
-  dirty_objects_.push_back(base::WrapUnique<DirtyObject>(dirty_object));
+  return std::next(dirty_objects_.insert(
+      insertion_point, base::WrapUnique<AXDirtyObject>(dirty_object)));
 }
 
 int RenderAccessibilityImpl::GetDeferredEventsDelay() {
@@ -818,11 +821,32 @@
             << " on node id " << event.id;
   }
 
-  // Now serialize all dirty objects. Keep track of IDs serialized
-  // so we don't have to serialize the same node twice.
+  // Dirty objects can be added as a result of serialization. For example,
+  // as children are iterated during depth first traversal in the serializer,
+  // the children sometimes need to be created. The initialization of these
+  // new children can lead to the discovery of parenting changes via
+  // aria-owns, or name changes on an ancestor that collects its name its from
+  // contents. In some cases this has led to an infinite loop, as the
+  // serialization of new dirty objects keeps adding new dirty objects to
+  // consider. The infinite loop is avoided by tracking the number of dirty
+  // objects that can be serialized from the loop, which is the initial
+  // number of dirty objects + kMaxExtraDirtyObjectsToSerialize.
+  // Allowing kMaxExtraDirtyObjectsToSerialize ensures that most important
+  // additional related changes occur at the same time, and that dump event
+  // tests have consistent results (the results change when dirty objects are
+  // processed in separate batches).
+  constexpr int kMaxExtraDirtyObjectsToSerialize = 100;
+  size_t num_remaining_objects_to_serialize =
+      dirty_objects_.size() + kMaxExtraDirtyObjectsToSerialize;
+
+  // Keep track of IDs serialized so we don't serialize the same node twice.
   std::set<int32_t> already_serialized_ids;
-  while (!dirty_objects_.empty()) {
-    std::unique_ptr<DirtyObject> current_dirty_object =
+
+  // Serialize all dirty objects in the list at this point in time, stopping
+  // either when the queue is empty, or the number of remaining objects to
+  // serialize has been reached.
+  while (!dirty_objects_.empty() && --num_remaining_objects_to_serialize > 0) {
+    std::unique_ptr<AXDirtyObject> current_dirty_object =
         std::move(dirty_objects_.front());
     dirty_objects_.pop_front();
     auto obj = current_dirty_object->obj;
@@ -858,6 +882,8 @@
     // mark all ancestors along the way as dirty.
     if (obj.AccessibilityIsIgnored()) {
       WebAXObject ancestor = obj;
+      std::list<std::unique_ptr<AXDirtyObject>>::iterator insertion_point =
+          std::next(dirty_objects_.begin());
       for (; !ancestor.IsDetached() && ancestor.AccessibilityIsIgnored();
            ancestor = ancestor.ParentObject()) {
         // There are 3 states of nodes that we care about here.
@@ -885,13 +911,22 @@
         // Similarly, during Event::kTextChanged, if any Ignored,
         // but included in tree ancestor uses NameFrom::kContents,
         // they must also be re-serialized in case the name changed.
-        EnqueueDirtyObject(ancestor, current_dirty_object->event_from,
-                           current_dirty_object->event_from_action,
-                           current_dirty_object->event_intents);
+        //
+        // Insert just after the object currently being serialized.
+        insertion_point = EnqueueDirtyObject(
+            ancestor, current_dirty_object->event_from,
+            current_dirty_object->event_from_action,
+            current_dirty_object->event_intents, insertion_point);
+        // Increment remaining objects to serialize to ensure that it's
+        // serialized now, not in a subsequent message.
+        ++num_remaining_objects_to_serialize;
       }
       EnqueueDirtyObject(ancestor, current_dirty_object->event_from,
                          current_dirty_object->event_from_action,
-                         current_dirty_object->event_intents);
+                         current_dirty_object->event_intents, insertion_point);
+      // Increment remaining objects to serialize to ensure that it's
+      // serialized now, not in a subsequent message.
+      ++num_remaining_objects_to_serialize;
     }
 
     ui::AXTreeUpdate update;
@@ -1472,9 +1507,8 @@
   last_ukm_url_ = "";
 }
 
-RenderAccessibilityImpl::DirtyObject::DirtyObject() = default;
-RenderAccessibilityImpl::DirtyObject::DirtyObject(const DirtyObject& other) =
-    default;
-RenderAccessibilityImpl::DirtyObject::~DirtyObject() = default;
+AXDirtyObject::AXDirtyObject() = default;
+AXDirtyObject::AXDirtyObject(const AXDirtyObject& other) = default;
+AXDirtyObject::~AXDirtyObject() = default;
 
 }  // namespace content
diff --git a/content/renderer/accessibility/render_accessibility_impl.h b/content/renderer/accessibility/render_accessibility_impl.h
index 860766f..04715d8 100644
--- a/content/renderer/accessibility/render_accessibility_impl.h
+++ b/content/renderer/accessibility/render_accessibility_impl.h
@@ -56,6 +56,16 @@
 
 using BlinkAXTreeSerializer = ui::AXTreeSerializer<blink::WebAXObject>;
 
+struct AXDirtyObject {
+  AXDirtyObject();
+  AXDirtyObject(const AXDirtyObject& other);
+  ~AXDirtyObject();
+  blink::WebAXObject obj;
+  ax::mojom::EventFrom event_from;
+  ax::mojom::Action event_from_action;
+  std::vector<ui::AXEventIntent> event_intents;
+};
+
 // The browser process implements native accessibility APIs, allowing assistive
 // technology (e.g., screen readers, magnifiers) to access and control the web
 // contents with high-level APIs. These APIs are also used by automation tools,
@@ -152,16 +162,6 @@
   int GetDeferredEventsDelay();
 
  private:
-  struct DirtyObject {
-    DirtyObject();
-    DirtyObject(const DirtyObject& other);
-    ~DirtyObject();
-    blink::WebAXObject obj;
-    ax::mojom::EventFrom event_from;
-    ax::mojom::Action event_from_action;
-    std::vector<ui::AXEventIntent> event_intents;
-  };
-
   enum class EventScheduleMode { kDeferEvents, kProcessEventsImmediately };
 
   enum class EventScheduleStatus {
@@ -175,6 +175,15 @@
     kNotWaiting
   };
 
+  // Add an AXDirtyObject to the dirty_objects_ queue.
+  // Returns an iterator pointing just after the newly inserted object.
+  std::list<std::unique_ptr<AXDirtyObject>>::iterator EnqueueDirtyObject(
+      const blink::WebAXObject& obj,
+      ax::mojom::EventFrom event_from,
+      ax::mojom::Action event_from_action,
+      std::vector<ui::AXEventIntent> event_intents,
+      std::list<std::unique_ptr<AXDirtyObject>>::iterator insertion_point);
+
   // Callback that will be called from the browser upon handling the message
   // previously sent to it via SendPendingAccessibilityEvents().
   void OnAccessibilityEventsHandled();
@@ -209,12 +218,6 @@
   bool ShouldSerializeNodeForEvent(const blink::WebAXObject& obj,
                                    const ui::AXEvent& event) const;
 
-  // Add a DirtyObject to the dirty_objects_ queue.
-  void EnqueueDirtyObject(const blink::WebAXObject& obj,
-                          ax::mojom::EventFrom event_from,
-                          ax::mojom::Action event_from_action,
-                          std::vector<ui::AXEventIntent> event_intents);
-
   // If we are calling this from a task, scheduling is allowed even if there is
   // a running task
   void ScheduleSendPendingAccessibilityEvents(
@@ -271,7 +274,7 @@
   // Objects that need to be re-serialized, the next time
   // we send an event bundle to the browser - but don't specifically need
   // an event fired.
-  std::list<std::unique_ptr<DirtyObject>> dirty_objects_;
+  std::list<std::unique_ptr<AXDirtyObject>> dirty_objects_;
 
   // The adapter that exposes Blink's accessibility tree to AXTreeSerializer.
   std::unique_ptr<BlinkAXTreeSource> tree_source_;
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc
index b42abfd..7dbd56f 100644
--- a/content/renderer/media/media_factory.cc
+++ b/content/renderer/media/media_factory.cc
@@ -4,6 +4,7 @@
 
 #include "content/renderer/media/media_factory.h"
 
+#include <memory>
 #include <string>
 #include <tuple>
 #include <utility>
@@ -150,7 +151,7 @@
   return kMaxWebMediaPlayers;
 }
 
-class FrameFetchContext : public media::ResourceFetchContext {
+class FrameFetchContext : public blink::ResourceFetchContext {
  public:
   explicit FrameFetchContext(blink::WebLocalFrame* frame) : frame_(frame) {
     DCHECK(frame_);
@@ -159,7 +160,7 @@
 
   blink::WebLocalFrame* frame() const { return frame_; }
 
-  // media::ResourceFetchContext implementation.
+  // blink::ResourceFetchContext implementation.
   std::unique_ptr<blink::WebAssociatedURLLoader> CreateUrlLoader(
       const blink::WebAssociatedURLLoaderOptions& options) override {
     return frame_->CreateAssociatedURLLoader(options);
@@ -481,7 +482,7 @@
   auto factory_selector = CreateRendererFactorySelector(
       media_log.get(), url, render_frame_->GetRenderFrameMediaPlaybackOptions(),
       GetDecoderFactory(),
-      std::make_unique<media::RemotePlaybackClientWrapperImpl>(client),
+      std::make_unique<blink::RemotePlaybackClientWrapperImpl>(client),
       &media_observer);
 
 #if BUILDFLAG(ENABLE_MEDIA_REMOTING)
@@ -491,7 +492,7 @@
   if (!fetch_context_) {
     fetch_context_ = std::make_unique<FrameFetchContext>(web_frame);
     DCHECK(!url_index_);
-    url_index_ = std::make_unique<media::UrlIndex>(
+    url_index_ = std::make_unique<blink::UrlIndex>(
         fetch_context_.get(),
         render_frame_->GetTaskRunner(blink::TaskType::kInternalMedia));
   }
@@ -502,7 +503,7 @@
   interface_broker_->GetInterface(
       metrics_provider.InitWithNewPipeAndPassReceiver());
 
-  std::unique_ptr<media::PowerStatusHelper> power_status_helper;
+  std::unique_ptr<blink::PowerStatusHelper> power_status_helper;
   if (base::FeatureList::IsEnabled(media::kMediaPowerExperiment)) {
     // The battery monitor is only available through the blink provider.
     // TODO(liberato): Should we expose this via |remote_interfaces_|?
@@ -518,7 +519,7 @@
           return battery_monitor;
         },
         remote_interfaces);
-    power_status_helper = std::make_unique<media::PowerStatusHelper>(
+    power_status_helper = std::make_unique<blink::PowerStatusHelper>(
         std::move(battery_monitor_cb));
   }
 
@@ -539,42 +540,39 @@
     return nullptr;
   }
 
-  std::unique_ptr<media::WebMediaPlayerParams> params(
-      new media::WebMediaPlayerParams(
-          std::move(media_log),
-          base::BindRepeating(&RenderFrameImpl::DeferMediaLoad,
-                              base::Unretained(render_frame_),
-                              delegate->has_played_media()),
-          audio_renderer_sink, media_task_runner,
-          render_thread->GetWorkerTaskRunner(),
-          render_thread->compositor_task_runner(),
-          video_frame_compositor_task_runner,
-          base::BindRepeating(
-              &v8::Isolate::AdjustAmountOfExternalAllocatedMemory,
-              base::Unretained(blink::MainThreadIsolate())),
-          initial_cdm, request_routing_token_cb_, media_observer,
-          enable_instant_source_buffer_gc, embedded_media_experience_enabled,
-          std::move(metrics_provider),
-          base::BindOnce(&blink::WebSurfaceLayerBridge::Create,
-                         parent_frame_sink_id,
-                         blink::WebSurfaceLayerBridge::ContainsVideo::kYes),
-          RenderThreadImpl::current()->SharedMainThreadContextProvider(),
-          surface_layer_mode,
-          render_frame_->GetRenderFrameMediaPlaybackOptions()
-              .is_background_suspend_enabled,
-          render_frame_->GetRenderFrameMediaPlaybackOptions()
-              .is_background_video_playback_enabled,
-          render_frame_->GetRenderFrameMediaPlaybackOptions()
-              .is_background_video_track_optimization_supported,
-          GetContentClient()->renderer()->OverrideDemuxerForUrl(
-              render_frame_, url, media_task_runner),
-          std::move(power_status_helper)));
+  auto params = std::make_unique<blink::WebMediaPlayerParams>(
+      std::move(media_log),
+      base::BindRepeating(&RenderFrameImpl::DeferMediaLoad,
+                          base::Unretained(render_frame_),
+                          delegate->has_played_media()),
+      audio_renderer_sink, media_task_runner,
+      render_thread->GetWorkerTaskRunner(),
+      render_thread->compositor_task_runner(),
+      video_frame_compositor_task_runner,
+      base::BindRepeating(&v8::Isolate::AdjustAmountOfExternalAllocatedMemory,
+                          base::Unretained(blink::MainThreadIsolate())),
+      initial_cdm, request_routing_token_cb_, media_observer,
+      enable_instant_source_buffer_gc, embedded_media_experience_enabled,
+      std::move(metrics_provider),
+      base::BindOnce(&blink::WebSurfaceLayerBridge::Create,
+                     parent_frame_sink_id,
+                     blink::WebSurfaceLayerBridge::ContainsVideo::kYes),
+      RenderThreadImpl::current()->SharedMainThreadContextProvider(),
+      surface_layer_mode,
+      render_frame_->GetRenderFrameMediaPlaybackOptions()
+          .is_background_suspend_enabled,
+      render_frame_->GetRenderFrameMediaPlaybackOptions()
+          .is_background_video_playback_enabled,
+      render_frame_->GetRenderFrameMediaPlaybackOptions()
+          .is_background_video_track_optimization_supported,
+      GetContentClient()->renderer()->OverrideDemuxerForUrl(render_frame_, url,
+                                                            media_task_runner),
+      std::move(power_status_helper));
 
-  std::unique_ptr<media::VideoFrameCompositor> vfc =
-      std::make_unique<media::VideoFrameCompositor>(
-          params->video_frame_compositor_task_runner(), std::move(submitter));
+  auto vfc = std::make_unique<blink::VideoFrameCompositor>(
+      params->video_frame_compositor_task_runner(), std::move(submitter));
 
-  media::WebMediaPlayerImpl* media_player = new media::WebMediaPlayerImpl(
+  auto* media_player = new blink::WebMediaPlayerImpl(
       web_frame, client, encrypted_client, delegate,
       std::move(factory_selector), url_index_.get(), std::move(vfc),
       std::move(params));
@@ -585,9 +583,9 @@
 blink::WebEncryptedMediaClient* MediaFactory::EncryptedMediaClient() {
   if (!web_encrypted_media_client_) {
     web_encrypted_media_client_ = std::make_unique<
-        media::WebEncryptedMediaClientImpl>(
+        blink::WebEncryptedMediaClientImpl>(
         GetCdmFactory(), render_frame_->GetMediaPermission(),
-        std::make_unique<media::KeySystemConfigSelector::WebLocalFrameDelegate>(
+        std::make_unique<blink::KeySystemConfigSelector::WebLocalFrameDelegate>(
             render_frame_->GetWebFrame()));
   }
   return web_encrypted_media_client_.get();
diff --git a/content/renderer/media/media_factory.h b/content/renderer/media/media_factory.h
index 4773b6c..dab7eaa 100644
--- a/content/renderer/media/media_factory.h
+++ b/content/renderer/media/media_factory.h
@@ -30,13 +30,16 @@
 
 namespace blink {
 class BrowserInterfaceBrokerProxy;
+class ResourceFetchContext;
+class UrlIndex;
 class WebContentDecryptionModule;
 class WebEncryptedMediaClient;
+class WebEncryptedMediaClientImpl;
 class WebLocalFrame;
 class WebMediaPlayer;
 class WebMediaPlayerClient;
 class WebMediaPlayerEncryptedMediaClient;
-}
+}  // namespace blink
 
 namespace cc {
 class LayerTreeSettings;
@@ -50,9 +53,6 @@
 class MediaObserver;
 class RemotePlaybackClientWrapper;
 class RendererWebMediaPlayerDelegate;
-class ResourceFetchContext;
-class UrlIndex;
-class WebEncryptedMediaClientImpl;
 }
 
 namespace content {
@@ -178,11 +178,11 @@
   std::unique_ptr<media::CdmFactory> cdm_factory_;
 
   // Media resource cache, lazily initialized.
-  std::unique_ptr<media::ResourceFetchContext> fetch_context_;
-  std::unique_ptr<media::UrlIndex> url_index_;
+  std::unique_ptr<blink::ResourceFetchContext> fetch_context_;
+  std::unique_ptr<blink::UrlIndex> url_index_;
 
   // EncryptedMediaClient attached to this frame; lazily initialized.
-  std::unique_ptr<media::WebEncryptedMediaClientImpl>
+  std::unique_ptr<blink::WebEncryptedMediaClientImpl>
       web_encrypted_media_client_;
 
 #if BUILDFLAG(ENABLE_MEDIA_REMOTING)
diff --git a/content/test/data/accessibility/event/add-alert-with-role-change-expected-android.txt b/content/test/data/accessibility/event/add-alert-with-role-change-expected-android.txt
new file mode 100644
index 0000000..cac32e5
--- /dev/null
+++ b/content/test/data/accessibility/event/add-alert-with-role-change-expected-android.txt
@@ -0,0 +1 @@
+TYPE_ANNOUNCEMENT - [This is an alert]
\ No newline at end of file
diff --git a/content/test/data/accessibility/event/same-page-link-navigation-expected-android.txt b/content/test/data/accessibility/event/same-page-link-navigation-expected-android.txt
new file mode 100644
index 0000000..b020e58
--- /dev/null
+++ b/content/test/data/accessibility/event/same-page-link-navigation-expected-android.txt
@@ -0,0 +1,3 @@
+TYPE_VIEW_SCROLLED
+TYPE_VIEW_CLICKED
+TYPE_VIEW_ACCESSIBILITY_FOCUSED
\ No newline at end of file
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 420cdfe..4cc7dc5 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -517,6 +517,9 @@
 crbug.com/1141066 [ mac apple-apple-m1 angle-opengl passthrough ] conformance/textures/misc/texture-copying-and-deletion.html [ Failure ]
 crbug.com/1141066 [ mac apple-apple-m1 angle-opengl passthrough ] conformance/textures/misc/texture-copying-feedback-loops.html [ Failure ]
 # finder:enable
+# Introduced by upstreaming Apple's direct-to-metal backend
+crbug.com/angleproject/5505 [ mac apple-angle-metal-renderer:-apple-m1 angle-metal ] conformance/context/context-attributes-alpha-depth-stencil-antialias.html [ Failure ]
+crbug.com/angleproject/5505 [ mac apple-angle-metal-renderer:-apple-m1 angle-metal ] conformance/rendering/multisample-corruption.html [ Failure ]
 
 ####################
 # Linux failures   #
diff --git a/content/test/mock_ssl_host_state_delegate.cc b/content/test/mock_ssl_host_state_delegate.cc
index c22300e..f02d074c 100644
--- a/content/test/mock_ssl_host_state_delegate.cc
+++ b/content/test/mock_ssl_host_state_delegate.cc
@@ -2,9 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/callback.h"
 #include "content/test/mock_ssl_host_state_delegate.h"
 
+#include "base/callback.h"
+#include "base/containers/contains.h"
+
 namespace content {
 
 MockSSLHostStateDelegate::MockSSLHostStateDelegate() {}
@@ -60,6 +62,14 @@
          hosts_ran_insecure_content_.end();
 }
 
+void MockSSLHostStateDelegate::AllowHttpForHost(const std::string& host) {
+  allow_http_hosts_.insert(host);
+}
+
+bool MockSSLHostStateDelegate::IsHttpAllowedForHost(const std::string& host) {
+  return base::Contains(allow_http_hosts_, host);
+}
+
 void MockSSLHostStateDelegate::RevokeUserAllowExceptions(
     const std::string& host) {
   exceptions_.erase(exceptions_.find(host));
diff --git a/content/test/mock_ssl_host_state_delegate.h b/content/test/mock_ssl_host_state_delegate.h
index f9efdda..87dac48 100644
--- a/content/test/mock_ssl_host_state_delegate.h
+++ b/content/test/mock_ssl_host_state_delegate.h
@@ -35,6 +35,10 @@
                                  int child_id,
                                  InsecureContentType content_type) override;
 
+  void AllowHttpForHost(const std::string& host) override;
+
+  bool IsHttpAllowedForHost(const std::string& host) override;
+
   void RevokeUserAllowExceptions(const std::string& host) override;
 
   bool HasAllowException(const std::string& host,
@@ -43,6 +47,7 @@
  private:
   std::set<std::string> exceptions_;
   std::set<std::string> hosts_ran_insecure_content_;
+  std::set<std::string> allow_http_hosts_;
 };
 
 }  // namespace content
diff --git a/device/vr/android/arcore/address_to_id_map.h b/device/vr/android/arcore/address_to_id_map.h
index 0bd03ce..3d69a41 100644
--- a/device/vr/android/arcore/address_to_id_map.h
+++ b/device/vr/android/arcore/address_to_id_map.h
@@ -20,7 +20,7 @@
 // be passed over mojo. The Ids should be directly exposable from blink if
 // desired.
 // Note that IdType must be constructable from a uint64_t, and should most often
-// be a util::IdTypeU64 type.
+// be a base::IdTypeU64 type.
 template <typename IdType>
 class AddressToIdMap {
  public:
diff --git a/device/vr/android/arcore/arcore_anchor_manager.h b/device/vr/android/arcore/arcore_anchor_manager.h
index 449a20c..5f1d4cab 100644
--- a/device/vr/android/arcore/arcore_anchor_manager.h
+++ b/device/vr/android/arcore/arcore_anchor_manager.h
@@ -7,8 +7,8 @@
 
 #include <map>
 
+#include "base/types/id_type.h"
 #include "base/types/pass_key.h"
-#include "base/util/type_safety/id_type.h"
 #include "device/vr/android/arcore/address_to_id_map.h"
 #include "device/vr/android/arcore/arcore_plane_manager.h"
 #include "device/vr/android/arcore/arcore_sdk.h"
@@ -20,7 +20,7 @@
 
 class ArCoreImpl;
 
-using AnchorId = util::IdTypeU64<class AnchorTag>;
+using AnchorId = base::IdTypeU64<class AnchorTag>;
 
 class ArCoreAnchorManager {
  public:
diff --git a/device/vr/android/arcore/arcore_impl.h b/device/vr/android/arcore/arcore_impl.h
index fcde19d..8293847 100644
--- a/device/vr/android/arcore/arcore_impl.h
+++ b/device/vr/android/arcore/arcore_impl.h
@@ -8,7 +8,7 @@
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "base/time/time.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "device/vr/android/arcore/arcore.h"
 #include "device/vr/android/arcore/arcore_anchor_manager.h"
 #include "device/vr/android/arcore/arcore_plane_manager.h"
@@ -22,7 +22,7 @@
 
 class ArCorePlaneManager;
 
-using AnchorId = util::IdTypeU64<class AnchorTag>;
+using AnchorId = base::IdTypeU64<class AnchorTag>;
 
 class CreateAnchorRequest {
  public:
diff --git a/device/vr/android/arcore/arcore_plane_manager.h b/device/vr/android/arcore/arcore_plane_manager.h
index 4c0e0ba..5e9672f 100644
--- a/device/vr/android/arcore/arcore_plane_manager.h
+++ b/device/vr/android/arcore/arcore_plane_manager.h
@@ -7,8 +7,8 @@
 
 #include <map>
 
+#include "base/types/id_type.h"
 #include "base/types/pass_key.h"
-#include "base/util/type_safety/id_type.h"
 #include "device/vr/android/arcore/address_to_id_map.h"
 #include "device/vr/android/arcore/arcore_sdk.h"
 #include "device/vr/android/arcore/scoped_arcore_objects.h"
@@ -20,7 +20,7 @@
 class ArCoreImpl;
 class ArCoreAnchorManager;
 
-using PlaneId = util::IdTypeU64<class PlaneTag>;
+using PlaneId = base::IdTypeU64<class PlaneTag>;
 
 std::pair<gfx::Quaternion, gfx::Point3F> GetPositionAndOrientationFromArPose(
     const ArSession* session,
diff --git a/device/vr/openxr/openxr_util.h b/device/vr/openxr/openxr_util.h
index 464791f..f53c48a 100644
--- a/device/vr/openxr/openxr_util.h
+++ b/device/vr/openxr/openxr_util.h
@@ -9,7 +9,7 @@
 #include <vector>
 
 #include "base/logging.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "device/vr/openxr/openxr_defs.h"
 #include "device/vr/openxr/openxr_extension_helper.h"
 #include "third_party/openxr/src/include/openxr/openxr.h"
@@ -17,7 +17,7 @@
 #include "ui/gfx/transform.h"
 #include "ui/gfx/transform_util.h"
 
-using AnchorId = util::IdTypeU64<class AnchorTag>;
+using AnchorId = base::IdTypeU64<class AnchorTag>;
 constexpr AnchorId kInvalidAnchorId;
 
 namespace device {
diff --git a/device/vr/util/hit_test_subscription_data.h b/device/vr/util/hit_test_subscription_data.h
index 67c58df..107d035 100644
--- a/device/vr/util/hit_test_subscription_data.h
+++ b/device/vr/util/hit_test_subscription_data.h
@@ -9,7 +9,7 @@
 
 namespace device {
 
-using HitTestSubscriptionId = util::IdTypeU64<class HitTestSubscriptionTag>;
+using HitTestSubscriptionId = base::IdTypeU64<class HitTestSubscriptionTag>;
 
 struct COMPONENT_EXPORT(DEVICE_VR_UTIL) HitTestSubscriptionData {
   mojom::XRNativeOriginInformationPtr native_origin_information;
diff --git a/docs/mac_lld.md b/docs/mac_lld.md
index 9f421a3..e461449f 100644
--- a/docs/mac_lld.md
+++ b/docs/mac_lld.md
@@ -13,8 +13,8 @@
 Chrome OS, Fuchsia), and it's faster than other COFF linkers (the executable
 file format on Windows).
 
-LLD is currently 3-4x faster at linking Chromium Framework than ld64, the macOS
-system linker in symbol\_level=0 release builds, despite ld64 being already
+LLD is currently 3-4x as fast as ld64, the macOS system linker, at linking
+Chromium Framework in symbol\_level=0 release builds, despite ld64 being already
 fast.
 
 LLD has advantages unrelated to speed, too:
diff --git a/extensions/browser/api/messaging/channel_endpoint.cc b/extensions/browser/api/messaging/channel_endpoint.cc
index e64d3a4..d176411 100644
--- a/extensions/browser/api/messaging/channel_endpoint.cc
+++ b/extensions/browser/api/messaging/channel_endpoint.cc
@@ -19,17 +19,15 @@
       render_process_id_(render_process_id),
       port_context_(port_context) {
   // Context must be exclusive to render frame or worker.
-  DCHECK_NE(port_context.is_for_service_worker(),
-            port_context.is_for_render_frame());
+  DCHECK(port_context.is_for_service_worker() ^
+         port_context.is_for_render_frame());
 }
 
 // For native message endpoint.
 ChannelEndpoint::ChannelEndpoint(content::BrowserContext* browser_context)
     : browser_context_(browser_context),
-      render_process_id_(content::ChildProcessHost::kInvalidUniqueID) {
-  DCHECK(!port_context_.is_for_render_frame() &&
-         !port_context_.is_for_service_worker());
-}
+      render_process_id_(content::ChildProcessHost::kInvalidUniqueID),
+      port_context_(PortContext::ForNativeHost()) {}
 
 bool ChannelEndpoint::is_for_service_worker() const {
   return port_context_.is_for_service_worker();
diff --git a/extensions/browser/api/messaging/channel_endpoint.h b/extensions/browser/api/messaging/channel_endpoint.h
index 62d6e87..0c95307 100644
--- a/extensions/browser/api/messaging/channel_endpoint.h
+++ b/extensions/browser/api/messaging/channel_endpoint.h
@@ -17,12 +17,12 @@
 namespace extensions {
 
 // Represents an endpoint (tab, frame or worker) of a message channel in a
-// render process.
+// render process or a native messaging host.
 // TODO(crbug.com/939594): Consolidate all classes/structs around extension
 // message ports.
 class ChannelEndpoint {
  public:
-  // An endpoint for a PortContext.
+  // An endpoint for a renderer PortContext.
   ChannelEndpoint(content::BrowserContext* browser_context,
                   int render_process_id,
                   const PortContext& port_context);
diff --git a/extensions/browser/api/messaging/extension_message_port.cc b/extensions/browser/api/messaging/extension_message_port.cc
index c14ef47e..6821512 100644
--- a/extensions/browser/api/messaging/extension_message_port.cc
+++ b/extensions/browser/api/messaging/extension_message_port.cc
@@ -390,22 +390,13 @@
                                      int routing_id,
                                      int worker_thread_id) {
   const bool is_for_service_worker = worker_thread_id != kMainThreadId;
-  if (!is_for_service_worker && routing_id == MSG_ROUTING_NONE) {
-    // The only non-frame-specific message is the response to an unhandled
-    // onConnect event in the extension process.
-    DCHECK(for_all_extension_contexts_);
-    ClearFrames();
-    if (!HasReceivers())
-      CloseChannel();
-    return;
-  }
+  DCHECK(is_for_service_worker || routing_id != MSG_ROUTING_NONE);
 
   if (is_for_service_worker) {
     UnregisterWorker(process_id, worker_thread_id);
-  } else {
-    DCHECK_NE(MSG_ROUTING_NONE, routing_id);
-    if (auto* rfh = content::RenderFrameHost::FromID(process_id, routing_id))
-      UnregisterFrame(rfh);
+  } else if (auto* rfh =
+                 content::RenderFrameHost::FromID(process_id, routing_id)) {
+    UnregisterFrame(rfh);
   }
 }
 
diff --git a/extensions/browser/api/messaging/extension_message_port.h b/extensions/browser/api/messaging/extension_message_port.h
index 925d8f2..6468162 100644
--- a/extensions/browser/api/messaging/extension_message_port.h
+++ b/extensions/browser/api/messaging/extension_message_port.h
@@ -49,7 +49,7 @@
                        bool include_child_frames);
 
   // Create a port that is tied to all frames and service workers of an
-  // extension.
+  // extension. Should only be used for a receiver port.
   static std::unique_ptr<ExtensionMessagePort> CreateForExtension(
       base::WeakPtr<ChannelDelegate> channel_delegate,
       const PortId& port_id,
diff --git a/extensions/browser/bad_message.h b/extensions/browser/bad_message.h
index 7a5c2f1..e56bf2c 100644
--- a/extensions/browser/bad_message.h
+++ b/extensions/browser/bad_message.h
@@ -41,6 +41,7 @@
   EMF_INVALID_EXTENSION_ID_FOR_EXTENSION_SOURCE = 15,
   EMF_INVALID_EXTENSION_ID_FOR_CONTENT_SCRIPT = 16,
   EMF_INVALID_EXTENSION_ID_FOR_WORKER_CONTEXT = 17,
+  EMF_INVALID_PORT_CONTEXT = 18,
   // Please add new elements here. The naming convention is abbreviated class
   // name (e.g. ExtensionHost becomes EH) plus a unique description of the
   // reason. After making changes, you MUST update histograms.xml by running:
diff --git a/extensions/browser/content_script_matching_browsertest.cc b/extensions/browser/content_script_matching_browsertest.cc
index c90c93c..1002dc1 100644
--- a/extensions/browser/content_script_matching_browsertest.cc
+++ b/extensions/browser/content_script_matching_browsertest.cc
@@ -340,9 +340,11 @@
   // Main frame should be matched.
   EXPECT_TRUE(DoContentScriptsMatch_Tab1_FooFrame());
 
-  // Subframe should not be matched (even though the patterns in the manifest do
-  // match bar.com).
-  EXPECT_FALSE(DoContentScriptsMatch_Tab1_BarFrame());
+  // Based on the `all_frames` from the manifest the subframe should not be
+  // matched (even though the patterns in the manifest do match bar.com).  OTOH,
+  // the URL Pattern matching in ContentScriptTracker ignroes `all_frames` and
+  // accepts additional false positives to solve extra corner cases.
+  EXPECT_TRUE(DoContentScriptsMatch_Tab1_BarFrame());
 }
 
 IN_PROC_BROWSER_TEST_F(ContentScriptMatchingBrowserTest,
diff --git a/extensions/browser/content_script_tracker.cc b/extensions/browser/content_script_tracker.cc
index 3b57569..260e78e 100644
--- a/extensions/browser/content_script_tracker.cc
+++ b/extensions/browser/content_script_tracker.cc
@@ -168,7 +168,8 @@
 // This function approximates a subset of checks from
 // UserScriptSet::GetInjectionForScript (which runs in the renderer process).
 // Unlike the renderer version, the code below doesn't consider ability to
-// create an injection host or the results of ScriptInjector::CanExecuteOnFrame.
+// create an injection host, nor the results of
+// ScriptInjector::CanExecuteOnFrame, nor the path of `url_patterns`.
 // Additionally the `effective_url` calculations are also only an approximation.
 // This is okay, because the top-level doc comment for ContentScriptTracker
 // documents that false positives are expected and why they are okay.
@@ -182,8 +183,7 @@
 
   GURL effective_url = GetEffectiveDocumentURL(
       frame, url, user_script.match_origin_as_fallback());
-  bool is_subframe = frame->GetParent();
-  return user_script.MatchesDocument(effective_url, is_subframe);
+  return user_script.url_patterns().MatchesSecurityOrigin(effective_url);
 }
 
 void HandleProgrammaticContentScriptInjection(
diff --git a/extensions/browser/extension_message_filter.cc b/extensions/browser/extension_message_filter.cc
index 2d90429a..c9aa439 100644
--- a/extensions/browser/extension_message_filter.cc
+++ b/extensions/browser/extension_message_filter.cc
@@ -373,6 +373,14 @@
   if (!browser_context_)
     return;
 
+  // Note, we need to add more stringent IPC validation here.
+  if (!port_context.is_for_render_frame() &&
+      !port_context.is_for_service_worker()) {
+    bad_message::ReceivedBadMessage(render_process_id_,
+                                    bad_message::EMF_INVALID_PORT_CONTEXT);
+    return;
+  }
+
   MessageService::Get(browser_context_)
       ->ClosePort(port_id, render_process_id_, port_context, force_close);
 }
diff --git a/extensions/common/api/declarative_net_request/constants.h b/extensions/common/api/declarative_net_request/constants.h
index d197aab..7bd27c11 100644
--- a/extensions/common/api/declarative_net_request/constants.h
+++ b/extensions/common/api/declarative_net_request/constants.h
@@ -5,7 +5,7 @@
 #ifndef EXTENSIONS_COMMON_API_DECLARATIVE_NET_REQUEST_CONSTANTS_H_
 #define EXTENSIONS_COMMON_API_DECLARATIVE_NET_REQUEST_CONSTANTS_H_
 
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 
 namespace extensions {
 namespace declarative_net_request {
@@ -23,7 +23,7 @@
 constexpr int kMinValidPriority = 1;
 
 using RulesetID =
-    ::util::IdType<class RulesetIDTag, int, -2 /* invalid value */>;
+    ::base::IdType<class RulesetIDTag, int, -2 /* invalid value */>;
 
 constexpr RulesetID kMinValidStaticRulesetID(1);
 constexpr RulesetID kDynamicRulesetID(0);
diff --git a/extensions/common/api/messaging/port_context.cc b/extensions/common/api/messaging/port_context.cc
index bc63268..68721794 100644
--- a/extensions/common/api/messaging/port_context.cc
+++ b/extensions/common/api/messaging/port_context.cc
@@ -6,23 +6,21 @@
 
 namespace extensions {
 
+PortContext::PortContext() = default;
+PortContext::~PortContext() = default;
+PortContext::PortContext(const PortContext& other) = default;
+
+PortContext::FrameContext::FrameContext(int routing_id)
+    : routing_id(routing_id) {}
+PortContext::FrameContext::FrameContext() = default;
+
 PortContext::WorkerContext::WorkerContext(int thread_id,
                                           int64_t version_id,
                                           const std::string& extension_id)
     : thread_id(thread_id),
       version_id(version_id),
       extension_id(extension_id) {}
-
-PortContext::FrameContext::FrameContext(int routing_id)
-    : routing_id(routing_id) {}
-
 PortContext::WorkerContext::WorkerContext() = default;
-PortContext::FrameContext::FrameContext() = default;
-
-PortContext::PortContext() = default;
-PortContext::~PortContext() = default;
-
-PortContext::PortContext(const PortContext& other) = default;
 
 PortContext PortContext::ForFrame(int routing_id) {
   PortContext context;
@@ -38,4 +36,8 @@
   return context;
 }
 
+PortContext PortContext::ForNativeHost() {
+  return PortContext();
+}
+
 }  // namespace extensions
diff --git a/extensions/common/api/messaging/port_context.h b/extensions/common/api/messaging/port_context.h
index 40ff0ed..b2e9f05 100644
--- a/extensions/common/api/messaging/port_context.h
+++ b/extensions/common/api/messaging/port_context.h
@@ -13,12 +13,14 @@
 
 namespace extensions {
 
-// Specifies the renderer context that is tied to a message Port.
 // A port can refer to a RenderFrame (FrameContext) or a Service Worker
-// (WorkerContext).
+// (WorkerContext) or a native messaging host.
 struct PortContext {
  public:
+  // This constructor is needed by our IPC code and tests. Clients should use
+  // factory functions instead.
   PortContext();
+
   ~PortContext();
   PortContext(const PortContext& other);
 
@@ -47,9 +49,11 @@
   static PortContext ForWorker(int thread_id,
                                int64_t version_id,
                                const std::string& extension_id);
+  static PortContext ForNativeHost();
 
   bool is_for_render_frame() const { return frame.has_value(); }
   bool is_for_service_worker() const { return worker.has_value(); }
+  bool is_for_native_host() const { return !frame && !worker; }
 
   absl::optional<FrameContext> frame;
   absl::optional<WorkerContext> worker;
diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc
index ad9b793..7b2e606dc 100644
--- a/extensions/common/manifest_constants.cc
+++ b/extensions/common/manifest_constants.cc
@@ -32,7 +32,6 @@
 const char kBluetooth[] = "bluetooth";
 const char kBookmarkUI[] = "bookmarks_ui";
 const char kBrowserAction[] = "browser_action";
-const char kChromeURLOverrides[] = "chrome_url_overrides";
 const char kCommands[] = "commands";
 const char kContentCapabilities[] = "content_capabilities";
 const char kContentSecurityPolicy[] = "content_security_policy";
diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h
index e968635..1e71052 100644
--- a/extensions/common/manifest_constants.h
+++ b/extensions/common/manifest_constants.h
@@ -35,7 +35,6 @@
 extern const char kBookmarkUI[];
 extern const char kBrowserAction[];
 extern const char kBrowseURLs[];
-extern const char kChromeURLOverrides[];
 extern const char kCommands[];
 extern const char kContentCapabilities[];
 extern const char kContentSecurityPolicy[];
diff --git a/fuchsia/engine/fidl/dev_tools.fidl b/fuchsia/engine/fidl/dev_tools.fidl
index 9ec371d..05bd554 100644
--- a/fuchsia/engine/fidl/dev_tools.fidl
+++ b/fuchsia/engine/fidl/dev_tools.fidl
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO(crbug.com/1227712): Migrate syntax and remove this.
+deprecated_syntax;
+
 library chromium.internal;
 
 using fuchsia.web;
diff --git a/fuchsia/fidl/cast/api_bindings.fidl b/fuchsia/fidl/cast/api_bindings.fidl
index d771523..d60cbca 100644
--- a/fuchsia/fidl/cast/api_bindings.fidl
+++ b/fuchsia/fidl/cast/api_bindings.fidl
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO(crbug.com/1227712): Migrate syntax and remove this.
+deprecated_syntax;
+
 library chromium.cast;
 
 using fuchsia.mem;
diff --git a/fuchsia/fidl/cast/application_config.fidl b/fuchsia/fidl/cast/application_config.fidl
index 0cb484a..0395075 100644
--- a/fuchsia/fidl/cast/application_config.fidl
+++ b/fuchsia/fidl/cast/application_config.fidl
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO(crbug.com/1227712): Migrate syntax and remove this.
+deprecated_syntax;
+
 library chromium.cast;
 
 using fuchsia.diagnostics;
diff --git a/fuchsia/fidl/cast/application_context.fidl b/fuchsia/fidl/cast/application_context.fidl
index 4a1027a6..8cfa9f6 100644
--- a/fuchsia/fidl/cast/application_context.fidl
+++ b/fuchsia/fidl/cast/application_context.fidl
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO(crbug.com/1227712): Migrate syntax and remove this.
+deprecated_syntax;
+
 library chromium.cast;
 
 [Discoverable]
diff --git a/fuchsia/fidl/cast/application_controller.fidl b/fuchsia/fidl/cast/application_controller.fidl
index 8e85c8f..27a5db0 100644
--- a/fuchsia/fidl/cast/application_controller.fidl
+++ b/fuchsia/fidl/cast/application_controller.fidl
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO(crbug.com/1227712): Migrate syntax and remove this.
+deprecated_syntax;
+
 library chromium.cast;
 
 using fuchsia.media.sessions2;
diff --git a/fuchsia/fidl/cast/cors_exempt_headers.fidl b/fuchsia/fidl/cast/cors_exempt_headers.fidl
index d46764c..b896f356 100644
--- a/fuchsia/fidl/cast/cors_exempt_headers.fidl
+++ b/fuchsia/fidl/cast/cors_exempt_headers.fidl
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO(crbug.com/1227712): Migrate syntax and remove this.
+deprecated_syntax;
+
 library chromium.cast;
 
 using fuchsia.web;
diff --git a/fuchsia/fidl/cast/data_reset.fidl b/fuchsia/fidl/cast/data_reset.fidl
index 481fd24..2772d14 100644
--- a/fuchsia/fidl/cast/data_reset.fidl
+++ b/fuchsia/fidl/cast/data_reset.fidl
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO(crbug.com/1227712): Migrate syntax and remove this.
+deprecated_syntax;
+
 library chromium.cast;
 
 // Used to request that persistent data for components hosted by the Runner is
@@ -19,4 +22,4 @@
   // the request could not be completed (for example, due to an I/O error).
   [Transitional]
   DeletePersistentData() -> (bool succeeded);
-};
\ No newline at end of file
+};
diff --git a/fuchsia/fidl/cast/url_request_rewriter.fidl b/fuchsia/fidl/cast/url_request_rewriter.fidl
index 45fd69e..e9f6d85 100644
--- a/fuchsia/fidl/cast/url_request_rewriter.fidl
+++ b/fuchsia/fidl/cast/url_request_rewriter.fidl
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO(crbug.com/1227712): Migrate syntax and remove this.
+deprecated_syntax;
+
 library chromium.cast;
 
 using fuchsia.web;
diff --git a/gpu/command_buffer/common/BUILD.gn b/gpu/command_buffer/common/BUILD.gn
index 16fca62..e8d71ac 100644
--- a/gpu/command_buffer/common/BUILD.gn
+++ b/gpu/command_buffer/common/BUILD.gn
@@ -62,7 +62,6 @@
   public_deps = [
     ":mailbox",
     "//base",
-    "//base/util/type_safety",
   ]
   configs += [ "//gpu:gpu_implementation" ]
 }
@@ -112,7 +111,7 @@
   public_deps = [
     ":common_base_sources",
     ":mailbox",
-    "//base/util/type_safety",
+    "//base",
     "//mojo/public/cpp/system",
     "//ui/gfx:memory_buffer",
     "//ui/gfx/geometry",
diff --git a/gpu/command_buffer/common/command_buffer_id.h b/gpu/command_buffer/common/command_buffer_id.h
index d3cacb2..3d6dc07 100644
--- a/gpu/command_buffer/common/command_buffer_id.h
+++ b/gpu/command_buffer/common/command_buffer_id.h
@@ -5,12 +5,12 @@
 #ifndef GPU_COMMAND_BUFFER_COMMON_COMMAND_BUFFER_ID_H_
 #define GPU_COMMAND_BUFFER_COMMON_COMMAND_BUFFER_ID_H_
 
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 
 namespace gpu {
 
 class CommandBuffer;
-using CommandBufferId = util::IdTypeU64<CommandBuffer>;
+using CommandBufferId = base::IdTypeU64<CommandBuffer>;
 
 }  // namespace gpu
 
diff --git a/gpu/command_buffer/common/discardable_handle.h b/gpu/command_buffer/common/discardable_handle.h
index dbc1149..b9bf3926b 100644
--- a/gpu/command_buffer/common/discardable_handle.h
+++ b/gpu/command_buffer/common/discardable_handle.h
@@ -6,7 +6,7 @@
 #define GPU_COMMAND_BUFFER_COMMON_DISCARDABLE_HANDLE_H_
 
 #include "base/memory/ref_counted.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "gpu/gpu_export.h"
 
 namespace gpu {
@@ -83,7 +83,7 @@
 // handle (via the constructor), and can Lock an existing handle.
 class GPU_EXPORT ClientDiscardableHandle : public DiscardableHandleBase {
  public:
-  using Id = util::IdType32<ClientDiscardableHandle>;
+  using Id = base::IdType32<ClientDiscardableHandle>;
 
   ClientDiscardableHandle();  // Constructs an invalid handle.
   ClientDiscardableHandle(scoped_refptr<Buffer> buffer,
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn
index dd61c1b49..82dbe7b 100644
--- a/gpu/command_buffer/service/BUILD.gn
+++ b/gpu/command_buffer/service/BUILD.gn
@@ -75,7 +75,7 @@
   ]
 
   public_deps = [
-    "//base/util/type_safety",
+    "//base",
     "//gpu/command_buffer/common:common_sources",
     "//url:url",
   ]
@@ -297,7 +297,7 @@
   include_dirs = [ "//third_party/mesa_headers" ]
 
   public_deps = [
-    "//base/util/type_safety",
+    "//base",
     "//cc/paint",
     "//gpu/command_buffer/common",
     "//gpu/command_buffer/common:gles2_sources",
diff --git a/gpu/command_buffer/service/external_vk_image_factory.cc b/gpu/command_buffer/service/external_vk_image_factory.cc
index ea8954b8..ef059046 100644
--- a/gpu/command_buffer/service/external_vk_image_factory.cc
+++ b/gpu/command_buffer/service/external_vk_image_factory.cc
@@ -161,7 +161,11 @@
                                          bool thread_safe,
                                          gfx::GpuMemoryBufferType gmb_type,
                                          GrContextType gr_context_type,
-                                         bool* allow_legacy_mailbox) {
+                                         bool* allow_legacy_mailbox,
+                                         bool is_pixel_used) {
+  if (is_pixel_used) {
+    return false;
+  }
   if (gmb_type != gfx::EMPTY_BUFFER && !CanImportGpuMemoryBuffer(gmb_type)) {
     return false;
   }
diff --git a/gpu/command_buffer/service/external_vk_image_factory.h b/gpu/command_buffer/service/external_vk_image_factory.h
index 1fde360..055aedc 100644
--- a/gpu/command_buffer/service/external_vk_image_factory.h
+++ b/gpu/command_buffer/service/external_vk_image_factory.h
@@ -65,7 +65,8 @@
                    bool thread_safe,
                    gfx::GpuMemoryBufferType gmb_type,
                    GrContextType gr_context_type,
-                   bool* allow_legacy_mailbox) override;
+                   bool* allow_legacy_mailbox,
+                   bool is_pixel_used) override;
 
  private:
   VkResult CreateExternalVkImage(VkFormat format,
diff --git a/gpu/command_buffer/service/sequence_id.h b/gpu/command_buffer/service/sequence_id.h
index e79ffc0..e59931a 100644
--- a/gpu/command_buffer/service/sequence_id.h
+++ b/gpu/command_buffer/service/sequence_id.h
@@ -5,12 +5,12 @@
 #ifndef GPU_COMMAND_BUFFER_SERVICE_SEQUENCE_ID_H_
 #define GPU_COMMAND_BUFFER_SERVICE_SEQUENCE_ID_H_
 
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 
 namespace gpu {
 
 class SyncPointOrderData;
-using SequenceId = util::IdTypeU32<SyncPointOrderData>;
+using SequenceId = base::IdTypeU32<SyncPointOrderData>;
 
 }  // namespace gpu
 
diff --git a/gpu/command_buffer/service/shared_image_backing_factory.h b/gpu/command_buffer/service/shared_image_backing_factory.h
index efc8eb5..c6a5365 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory.h
+++ b/gpu/command_buffer/service/shared_image_backing_factory.h
@@ -74,7 +74,8 @@
                            bool thread_safe,
                            gfx::GpuMemoryBufferType gmb_type,
                            GrContextType gr_context_type,
-                           bool* allow_legacy_mailbox) = 0;
+                           bool* allow_legacy_mailbox,
+                           bool is_pixel_used) = 0;
 };
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc
index 897e705..745b7450 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc
@@ -745,7 +745,11 @@
     bool thread_safe,
     gfx::GpuMemoryBufferType gmb_type,
     GrContextType gr_context_type,
-    bool* allow_legacy_mailbox) {
+    bool* allow_legacy_mailbox,
+    bool is_pixel_used) {
+  if (is_pixel_used) {
+    return false;
+  }
   if (gmb_type != gfx::EMPTY_BUFFER && !CanImportGpuMemoryBuffer(gmb_type)) {
     return false;
   }
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.h b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.h
index 2961be7..d90fa27d 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.h
+++ b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.h
@@ -68,7 +68,8 @@
                    bool thread_safe,
                    gfx::GpuMemoryBufferType gmb_type,
                    GrContextType gr_context_type,
-                   bool* allow_legacy_mailbox) override;
+                   bool* allow_legacy_mailbox,
+                   bool is_pixel_used) override;
   bool IsFormatSupported(viz::ResourceFormat format);
 
  private:
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_d3d.cc b/gpu/command_buffer/service/shared_image_backing_factory_d3d.cc
index ce573b5..ede1f0f 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_d3d.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_d3d.cc
@@ -430,7 +430,11 @@
     bool thread_safe,
     gfx::GpuMemoryBufferType gmb_type,
     GrContextType gr_context_type,
-    bool* allow_legacy_mailbox) {
+    bool* allow_legacy_mailbox,
+    bool is_pixel_used) {
+  if (is_pixel_used) {
+    return false;
+  }
   if (gmb_type != gfx::EMPTY_BUFFER && !CanImportGpuMemoryBuffer(gmb_type)) {
     return false;
   }
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_d3d.h b/gpu/command_buffer/service/shared_image_backing_factory_d3d.h
index 0c3d4afc..abc82e4 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_d3d.h
+++ b/gpu/command_buffer/service/shared_image_backing_factory_d3d.h
@@ -103,7 +103,8 @@
                    bool thread_safe,
                    gfx::GpuMemoryBufferType gmb_type,
                    GrContextType gr_context_type,
-                   bool* allow_legacy_mailbox) override;
+                   bool* allow_legacy_mailbox,
+                   bool is_pixel_used) override;
 
   // Returns true if the specified GpuMemoryBufferType can be imported using
   // this factory.
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_egl.cc b/gpu/command_buffer/service/shared_image_backing_factory_egl.cc
index a1e45b2..5dd5ba1 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_egl.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_egl.cc
@@ -133,7 +133,11 @@
     bool thread_safe,
     gfx::GpuMemoryBufferType gmb_type,
     GrContextType gr_context_type,
-    bool* allow_legacy_mailbox) {
+    bool* allow_legacy_mailbox,
+    bool is_pixel_used) {
+  if (is_pixel_used) {
+    return false;
+  }
   // Doesn't support gmb for now
   if (gmb_type != gfx::EMPTY_BUFFER) {
     return false;
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_egl.h b/gpu/command_buffer/service/shared_image_backing_factory_egl.h
index 8aa192d..6e24953e 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_egl.h
+++ b/gpu/command_buffer/service/shared_image_backing_factory_egl.h
@@ -76,7 +76,8 @@
                    bool thread_safe,
                    gfx::GpuMemoryBufferType gmb_type,
                    GrContextType gr_context_type,
-                   bool* allow_legacy_mailbox) override;
+                   bool* allow_legacy_mailbox,
+                   bool is_pixel_used) override;
 
  private:
   std::unique_ptr<SharedImageBacking> MakeEglImageBacking(
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_image.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_image.cc
index 6087b40..b46b2ef 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_image.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_image.cc
@@ -247,7 +247,11 @@
     bool thread_safe,
     gfx::GpuMemoryBufferType gmb_type,
     GrContextType gr_context_type,
-    bool* allow_legacy_mailbox) {
+    bool* allow_legacy_mailbox,
+    bool is_pixel_used) {
+  if (is_pixel_used && gr_context_type != GrContextType::kGL) {
+    return false;
+  }
   if (thread_safe) {
     return false;
   }
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_image.h b/gpu/command_buffer/service/shared_image_backing_factory_gl_image.h
index b15b6a3e..276c45c 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_image.h
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_image.h
@@ -78,7 +78,8 @@
                    bool thread_safe,
                    gfx::GpuMemoryBufferType gmb_type,
                    GrContextType gr_context_type,
-                   bool* allow_legacy_mailbox) override;
+                   bool* allow_legacy_mailbox,
+                   bool is_pixel_used) override;
 
  private:
   scoped_refptr<gl::GLImage> MakeGLImage(int client_id,
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
index 40df994..480b2df4 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
@@ -123,7 +123,11 @@
     bool thread_safe,
     gfx::GpuMemoryBufferType gmb_type,
     GrContextType gr_context_type,
-    bool* allow_legacy_mailbox) {
+    bool* allow_legacy_mailbox,
+    bool is_pixel_used) {
+  if (is_pixel_used && gr_context_type != GrContextType::kGL) {
+    return false;
+  }
   if (thread_safe) {
     return false;
   }
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h
index 07758c075..ed5a806 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h
@@ -75,7 +75,8 @@
                    bool thread_safe,
                    gfx::GpuMemoryBufferType gmb_type,
                    GrContextType gr_context_type,
-                   bool* allow_legacy_mailbox) override;
+                   bool* allow_legacy_mailbox,
+                   bool is_pixel_used) override;
 
   static std::unique_ptr<SharedImageBacking> CreateSharedImageForTest(
       const Mailbox& mailbox,
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_ozone.cc b/gpu/command_buffer/service/shared_image_backing_factory_ozone.cc
index 6083ab3..25b3241 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_ozone.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_ozone.cc
@@ -79,7 +79,11 @@
     bool thread_safe,
     gfx::GpuMemoryBufferType gmb_type,
     GrContextType gr_context_type,
-    bool* allow_legacy_mailbox) {
+    bool* allow_legacy_mailbox,
+    bool is_pixel_used) {
+  if (is_pixel_used) {
+    return false;
+  }
   // Doesn't support gmb for now
   if (gmb_type != gfx::EMPTY_BUFFER) {
     return false;
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_ozone.h b/gpu/command_buffer/service/shared_image_backing_factory_ozone.h
index f910d25..892e6ec 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_ozone.h
+++ b/gpu/command_buffer/service/shared_image_backing_factory_ozone.h
@@ -65,7 +65,8 @@
                    bool thread_safe,
                    gfx::GpuMemoryBufferType gmb_type,
                    GrContextType gr_context_type,
-                   bool* allow_legacy_mailbox) override;
+                   bool* allow_legacy_mailbox,
+                   bool is_pixel_used) override;
 
  private:
   SharedContextState* const shared_context_state_;
diff --git a/gpu/command_buffer/service/shared_image_factory.cc b/gpu/command_buffer/service/shared_image_factory.cc
index 6ae8714..c6c87a98 100644
--- a/gpu/command_buffer/service/shared_image_factory.cc
+++ b/gpu/command_buffer/service/shared_image_factory.cc
@@ -234,7 +234,8 @@
                                            gpu::SurfaceHandle surface_handle,
                                            uint32_t usage) {
   bool allow_legacy_mailbox = false;
-  auto* factory = GetFactoryByUsage(usage, format, &allow_legacy_mailbox);
+  auto* factory = GetFactoryByUsage(usage, format, &allow_legacy_mailbox,
+                                    /*is_pixel_used=*/false);
   if (!factory)
     return false;
   auto backing = factory->CreateSharedImage(
@@ -262,25 +263,13 @@
     return false;
   }
 
-  // Currently we only perform data uploads via two paths,
-  // |gl_backing_factory_| for GL and |wrapped_sk_image_factory_| for Vulkan and
-  // Dawn.
-  // TODO(ericrk): Make this generic in the future.
   bool allow_legacy_mailbox = false;
   SharedImageBackingFactory* factory = nullptr;
   if (backing_factory_for_testing_) {
     factory = backing_factory_for_testing_;
-  } else if (gr_context_type_ == GrContextType::kGL) {
-    allow_legacy_mailbox = true;
-    if (gl_texture_backing_factory_->IsSupported(
-            usage, format, IsSharedBetweenThreads(usage), gfx::EMPTY_BUFFER,
-            gr_context_type_, &allow_legacy_mailbox)) {
-      factory = gl_texture_backing_factory_.get();
-    } else {
-      factory = gl_image_backing_factory_.get();
-    }
   } else {
-    factory = wrapped_sk_image_factory_.get();
+    factory = GetFactoryByUsage(usage, format, &allow_legacy_mailbox,
+                                /*is_pixel_used=*/true, gfx::EMPTY_BUFFER);
   }
   if (!factory)
     return false;
@@ -307,8 +296,9 @@
   // factory, e.g. SharedImageBackingFactoryAHB.
   bool allow_legacy_mailbox = false;
   auto resource_format = viz::GetResourceFormat(format);
-  auto* factory = GetFactoryByUsage(usage, resource_format,
-                                    &allow_legacy_mailbox, handle.type);
+  auto* factory =
+      GetFactoryByUsage(usage, resource_format, &allow_legacy_mailbox,
+                        /*is_pixel_used=*/false, handle.type);
   if (!factory)
     return false;
   auto backing = factory->CreateSharedImage(
@@ -518,6 +508,7 @@
     uint32_t usage,
     viz::ResourceFormat format,
     bool* allow_legacy_mailbox,
+    bool is_pixel_used,
     gfx::GpuMemoryBufferType gmb_type) {
   if (backing_factory_for_testing_)
     return backing_factory_for_testing_;
@@ -527,14 +518,14 @@
   if (wrapped_sk_image_factory_ &&
       wrapped_sk_image_factory_->IsSupported(
           usage, format, share_between_threads, gmb_type, gr_context_type_,
-          allow_legacy_mailbox)) {
+          allow_legacy_mailbox, is_pixel_used)) {
     return wrapped_sk_image_factory_.get();
   }
 
   if (gl_texture_backing_factory_ &&
       gl_texture_backing_factory_->IsSupported(
           usage, format, share_between_threads, gmb_type, gr_context_type_,
-          allow_legacy_mailbox)) {
+          allow_legacy_mailbox, is_pixel_used)) {
     return gl_texture_backing_factory_.get();
   }
 
@@ -542,7 +533,7 @@
   if (egl_backing_factory_ &&
       egl_backing_factory_->IsSupported(usage, format, share_between_threads,
                                         gmb_type, gr_context_type_,
-                                        allow_legacy_mailbox)) {
+                                        allow_legacy_mailbox, is_pixel_used)) {
     return egl_backing_factory_.get();
   }
 #endif  // !defined(OS_ANDROID)
@@ -550,7 +541,7 @@
   if (interop_backing_factory_ &&
       interop_backing_factory_->IsSupported(
           usage, format, share_between_threads, gmb_type, gr_context_type_,
-          allow_legacy_mailbox)) {
+          allow_legacy_mailbox, is_pixel_used)) {
     return interop_backing_factory_.get();
   }
 
@@ -560,14 +551,14 @@
   if (external_vk_image_factory_ &&
       external_vk_image_factory_->IsSupported(
           usage, format, share_between_threads, gmb_type, gr_context_type_,
-          allow_legacy_mailbox))
+          allow_legacy_mailbox, is_pixel_used))
     return external_vk_image_factory_.get();
 #endif  // !defined(OS_ANDROID)
 
   if (gl_image_backing_factory_ &&
       gl_image_backing_factory_->IsSupported(
           usage, format, share_between_threads, gmb_type, gr_context_type_,
-          allow_legacy_mailbox)) {
+          allow_legacy_mailbox, is_pixel_used)) {
     return gl_image_backing_factory_.get();
   }
 
diff --git a/gpu/command_buffer/service/shared_image_factory.h b/gpu/command_buffer/service/shared_image_factory.h
index 84b8463..bca02c6e 100644
--- a/gpu/command_buffer/service/shared_image_factory.h
+++ b/gpu/command_buffer/service/shared_image_factory.h
@@ -155,6 +155,7 @@
       uint32_t usage,
       viz::ResourceFormat format,
       bool* allow_legacy_mailbox,
+      bool is_pixel_used,
       gfx::GpuMemoryBufferType gmb_type = gfx::EMPTY_BUFFER);
 
   MailboxManager* mailbox_manager_;
diff --git a/gpu/command_buffer/service/wrapped_sk_image.cc b/gpu/command_buffer/service/wrapped_sk_image.cc
index aedafe1..c89c848 100644
--- a/gpu/command_buffer/service/wrapped_sk_image.cc
+++ b/gpu/command_buffer/service/wrapped_sk_image.cc
@@ -82,7 +82,7 @@
       SkPixmap pixmap(info, shared_memory_wrapper_.GetMemory(),
                       shared_memory_wrapper_.GetStride());
       if (!context_state_->gr_context()->updateBackendTexture(
-              backend_texture_, &pixmap, /*levels=*/1, nullptr, nullptr)) {
+              backend_texture_, &pixmap, /*numLevels=*/1, nullptr, nullptr)) {
         DLOG(ERROR) << "Failed to update WrappedSkImage texture";
       }
     }
@@ -512,7 +512,12 @@
                                         bool thread_safe,
                                         gfx::GpuMemoryBufferType gmb_type,
                                         GrContextType gr_context_type,
-                                        bool* allow_legacy_mailbox) {
+                                        bool* allow_legacy_mailbox,
+                                        bool is_pixel_used) {
+  // TODO(hitawala): Remove gr_context_type check for supporting GL as well.
+  if (is_pixel_used && gr_context_type == GrContextType::kGL) {
+    return false;
+  }
   if (!CanUseWrappedSkImage(usage, gr_context_type) || thread_safe) {
     return false;
   }
diff --git a/gpu/command_buffer/service/wrapped_sk_image.h b/gpu/command_buffer/service/wrapped_sk_image.h
index d1b903e..55a66c0 100644
--- a/gpu/command_buffer/service/wrapped_sk_image.h
+++ b/gpu/command_buffer/service/wrapped_sk_image.h
@@ -65,7 +65,8 @@
                    bool thread_safe,
                    gfx::GpuMemoryBufferType gmb_type,
                    GrContextType gr_context_type,
-                   bool* allow_legacy_mailbox) override;
+                   bool* allow_legacy_mailbox,
+                   bool is_pixel_used) override;
 
  private:
   bool CanImportGpuMemoryBuffer(gfx::GpuMemoryBufferType memory_buffer_type);
diff --git a/gpu/config/gpu_info_collector.h b/gpu/config/gpu_info_collector.h
index 4e66fcc9..24cb745 100644
--- a/gpu/config/gpu_info_collector.h
+++ b/gpu/config/gpu_info_collector.h
@@ -73,9 +73,12 @@
 #if defined(OS_WIN)
 // Collect the DirectX Disagnostics information about the attached displays.
 GPU_EXPORT bool GetDxDiagnostics(DxDiagNode* output);
-GPU_EXPORT uint32_t GetGpuSupportedD3D12Version();
+GPU_EXPORT void GetGpuSupportedD3D12Version(
+    uint32_t& d3d12_feature_level,
+    uint32_t& highest_shader_model_version);
 GPU_EXPORT void RecordGpuSupportedDx12VersionHistograms(
-    uint32_t d3d12_feature_level);
+    uint32_t d3d12_feature_level,
+    uint32_t highest_shader_model_version);
 GPU_EXPORT uint32_t
 GetGpuSupportedVulkanVersion(const gpu::GPUInfo::GPUDevice& gpu_device);
 
diff --git a/gpu/config/gpu_info_collector_win.cc b/gpu/config/gpu_info_collector_win.cc
index 617d5f164..19e5d4d 100644
--- a/gpu/config/gpu_info_collector_win.cc
+++ b/gpu/config/gpu_info_collector_win.cc
@@ -89,6 +89,48 @@
   }
 }
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class D3D12ShaderModel {
+  kUnknownOrNoD3D12Devices = 0,
+  kD3DShaderModel_5_1 = 1,
+  kD3DShaderModel_6_0 = 2,
+  kD3DShaderModel_6_1 = 3,
+  kD3DShaderModel_6_2 = 4,
+  kD3DShaderModel_6_3 = 5,
+  kD3DShaderModel_6_4 = 6,
+  kD3DShaderModel_6_5 = 7,
+  kD3DShaderModel_6_6 = 8,
+  kMaxValue = kD3DShaderModel_6_6,
+};
+
+D3D12ShaderModel ConvertToHistogramShaderVersion(uint32_t version) {
+  switch (version) {
+    case 0:
+      return D3D12ShaderModel::kUnknownOrNoD3D12Devices;
+    case D3D_SHADER_MODEL_5_1:
+      return D3D12ShaderModel::kD3DShaderModel_5_1;
+    case D3D_SHADER_MODEL_6_0:
+      return D3D12ShaderModel::kD3DShaderModel_6_0;
+    case D3D_SHADER_MODEL_6_1:
+      return D3D12ShaderModel::kD3DShaderModel_6_1;
+    case D3D_SHADER_MODEL_6_2:
+      return D3D12ShaderModel::kD3DShaderModel_6_2;
+    case D3D_SHADER_MODEL_6_3:
+      return D3D12ShaderModel::kD3DShaderModel_6_3;
+    case D3D_SHADER_MODEL_6_4:
+      return D3D12ShaderModel::kD3DShaderModel_6_4;
+    case D3D_SHADER_MODEL_6_5:
+      return D3D12ShaderModel::kD3DShaderModel_6_5;
+    case D3D_SHADER_MODEL_6_6:
+      return D3D12ShaderModel::kD3DShaderModel_6_6;
+
+    default:
+      NOTREACHED();
+      return D3D12ShaderModel::kUnknownOrNoD3D12Devices;
+  }
+}
+
 OverlaySupport FlagsToOverlaySupport(bool overlays_supported, UINT flags) {
   if (flags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING)
     return OverlaySupport::kScaling;
@@ -251,13 +293,18 @@
 }
 
 // DirectX 12 are included with Windows 10 and Server 2016.
-uint32_t GetGpuSupportedD3D12Version() {
+void GetGpuSupportedD3D12Version(uint32_t& d3d12_feature_level,
+                                 uint32_t& highest_shader_model_version) {
   TRACE_EVENT0("gpu", "GetGpuSupportedD3D12Version");
 
+  // Initialize to 0 to indicated an unknown type in UMA.
+  d3d12_feature_level = 0;
+  highest_shader_model_version = 0;
+
   base::ScopedNativeLibrary d3d12_library(
       base::FilePath(FILE_PATH_LITERAL("d3d12.dll")));
   if (!d3d12_library.is_valid())
-    return 0;
+    return;
 
   // The order of feature levels to attempt to create in D3D CreateDevice
   const D3D_FEATURE_LEVEL_CHROMIUM feature_levels[] = {
@@ -268,18 +315,29 @@
   PFN_D3D12_CREATE_DEVICE_CHROMIUM D3D12CreateDevice =
       reinterpret_cast<PFN_D3D12_CREATE_DEVICE_CHROMIUM>(
           d3d12_library.GetFunctionPointer("D3D12CreateDevice"));
+  Microsoft::WRL::ComPtr<ID3D12Device> d3d12_device;
   if (D3D12CreateDevice) {
     // For the default adapter only. (*pAdapter == nullptr)
-    // Check to see if the adapter supports Direct3D 12, but don't create the
-    // actual device yet. (**ppDevice == nullptr)
+    // Check to see if the adapter supports Direct3D 12.
     for (auto level : feature_levels) {
       if (SUCCEEDED(D3D12CreateDevice(nullptr, level, _uuidof(ID3D12Device),
-                                      nullptr))) {
-        return level;
+                                      &d3d12_device))) {
+        d3d12_feature_level = level;
+        break;
       }
     }
   }
-  return 0;
+
+  // Query the maximum supported shader model version.
+  if (d3d12_device) {
+    D3D12_FEATURE_DATA_SHADER_MODEL shader_model_data = {};
+    shader_model_data.HighestShaderModel = D3D_SHADER_MODEL_6_6;
+    if (SUCCEEDED(d3d12_device->CheckFeatureSupport(
+            D3D12_FEATURE_SHADER_MODEL, &shader_model_data,
+            sizeof(shader_model_data)))) {
+      highest_shader_model_version = shader_model_data.HighestShaderModel;
+    }
+  }
 }
 
 // The old graphics drivers are installed to the Windows system directory
@@ -499,13 +557,19 @@
   return 0;
 }
 
-void RecordGpuSupportedDx12VersionHistograms(uint32_t d3d12_feature_level) {
+void RecordGpuSupportedDx12VersionHistograms(
+    uint32_t d3d12_feature_level,
+    uint32_t highest_shader_model_version) {
   bool supports_dx12 =
       (d3d12_feature_level >= D3D_FEATURE_LEVEL_12_0) ? true : false;
   UMA_HISTOGRAM_BOOLEAN("GPU.SupportsDX12", supports_dx12);
   UMA_HISTOGRAM_ENUMERATION(
       "GPU.D3D12FeatureLevel",
       ConvertToHistogramFeatureLevel(d3d12_feature_level));
+
+  UMA_HISTOGRAM_ENUMERATION(
+      "GPU.D3D12HighestShaderModel",
+      ConvertToHistogramShaderVersion(highest_shader_model_version));
 }
 
 bool CollectD3D11FeatureInfo(D3D_FEATURE_LEVEL* d3d11_feature_level,
diff --git a/gpu/vulkan/BUILD.gn b/gpu/vulkan/BUILD.gn
index b91be74..66ad936 100644
--- a/gpu/vulkan/BUILD.gn
+++ b/gpu/vulkan/BUILD.gn
@@ -105,7 +105,6 @@
     deps = [
       ":buildflags",
       "//base",
-      "//base/util/type_safety",
       "//build:chromeos_buildflags",
       "//components/crash/core/common:crash_key",
       "//gpu/ipc/common:vulkan_ycbcr_info",
diff --git a/gpu/vulkan/win32/BUILD.gn b/gpu/vulkan/win32/BUILD.gn
index f0144ca5..d06ce69 100644
--- a/gpu/vulkan/win32/BUILD.gn
+++ b/gpu/vulkan/win32/BUILD.gn
@@ -27,7 +27,7 @@
   public_configs = [ ":vulkan_win32" ]
 
   deps = [
-    "//base/util/type_safety",
+    "//base",
     "//ui/gfx",
   ]
 
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index d8f21c5..4db3b03 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -28519,7 +28519,7 @@
         cipd_version: "refs/heads/master"
         cmd: "luciexe"
       }
-      properties: "{\"$build/code_coverage\":{\"coverage_reference_commit\":\"c942891373445199f69afd905965ad1e89cdee09\",\"coverage_test_types\":[\"overall\",\"unit\"],\"use_clang_coverage\":true},\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"$recipe_engine/resultdb/test_presentation\":{\"column_keys\":[],\"grouping_keys\":[\"status\",\"v.test_suite\"]},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
+      properties: "{\"$build/code_coverage\":{\"coverage_test_types\":[\"overall\",\"unit\"],\"use_clang_coverage\":true},\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"$recipe_engine/resultdb/test_presentation\":{\"column_keys\":[],\"grouping_keys\":[\"status\",\"v.test_suite\"]},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
       execution_timeout_secs: 72000
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
@@ -62209,7 +62209,7 @@
         cipd_version: "refs/heads/master"
         cmd: "luciexe"
       }
-      properties: "{\"$build/goma\":{\"enable_ats\":false,\"jobs\":300,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"$recipe_engine/resultdb/test_presentation\":{\"column_keys\":[],\"grouping_keys\":[\"status\",\"v.test_suite\"]},\"builder_group\":\"tryserver.chromium.win\",\"orchestrator\":{\"builder_group\":\"tryserver.chromium.win\",\"builder_name\":\"win10-rel-orchestrator\"},\"recipe\":\"chromium/compilator\"}"
+      properties: "{\"$build/code_coverage\":{\"coverage_test_types\":[\"unit\",\"overall\"],\"use_clang_coverage\":true},\"$build/goma\":{\"enable_ats\":false,\"jobs\":300,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"$recipe_engine/resultdb/test_presentation\":{\"column_keys\":[],\"grouping_keys\":[\"status\",\"v.test_suite\"]},\"builder_group\":\"tryserver.chromium.win\",\"orchestrator\":{\"builder_group\":\"tryserver.chromium.win\",\"builder_name\":\"win10-rel-orchestrator\"},\"recipe\":\"chromium/compilator\"}"
       execution_timeout_secs: 14400
       expiration_secs: 7200
       grace_period {
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index 7aa5218e..5f04af8d 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -4211,8 +4211,6 @@
     ),
     use_clang_coverage = True,
     coverage_test_types = ["overall", "unit"],
-    # last commit of 2020
-    coverage_reference_commit = "c942891373445199f69afd905965ad1e89cdee09",
     triggered_by = [],
 )
 
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index aa99df9..b2d3fb46 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -1882,6 +1882,8 @@
     ssd = True,
     goma_jobs = goma.jobs.J300,
     executable = "recipe:chromium/compilator",
+    use_clang_coverage = True,
+    coverage_test_types = ["unit", "overall"],
     properties = {
         "orchestrator": {
             "builder_name": "win10-rel-orchestrator",
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index a33d6d9..7110d44 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -931,7 +931,7 @@
         Built for your iPhone
       </message>
       <message name="IDS_IOS_FIRST_RUN_WELCOME_SCREEN_MANAGED" desc="The disclaimer that the browser is managed on the welcome screen presented to the user on First Run when the browser is managed [iOS only]">
-        This browser is managed by your organization
+        Your administrator has control over Chrome and can access its data
       </message>
       <message name="IDS_IOS_FIRSTRUN_BROWSER_MANAGED" desc="Label explaining that the app is managed by an organization. This is shown on the Welcome screen presented to the user on First Run. Only shown if some enterprise policies are set. [iOS only]">
         Your browser is managed by your organization. Some features may be disabled.
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_WELCOME_SCREEN_MANAGED.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_WELCOME_SCREEN_MANAGED.png.sha1
index ce4b527..b55601a 100644
--- a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_WELCOME_SCREEN_MANAGED.png.sha1
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_WELCOME_SCREEN_MANAGED.png.sha1
@@ -1 +1 @@
-16a391cece53bfa37bf6d7cbd2ece31659f5828f
\ No newline at end of file
+900afbe5e51b0a6c3d75a600d6433e69e23c2bd5
\ No newline at end of file
diff --git a/ios/chrome/browser/autofill/autofill_controller_unittest.mm b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
index 4ea0525..3044014 100644
--- a/ios/chrome/browser/autofill/autofill_controller_unittest.mm
+++ b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
@@ -279,7 +279,7 @@
 
   accessory_mediator_ =
       [[FormInputAccessoryMediator alloc] initWithConsumer:nil
-                                                  delegate:nil
+                                                   handler:nil
                                               webStateList:NULL
                                        personalDataManager:NULL
                                              passwordStore:nullptr
diff --git a/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm b/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm
index 02a488e..bc07a7bb 100644
--- a/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm
+++ b/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm
@@ -205,7 +205,7 @@
 
     accessory_mediator_ =
         [[FormInputAccessoryMediator alloc] initWithConsumer:mock_consumer
-                                                    delegate:nil
+                                                     handler:nil
                                                 webStateList:NULL
                                          personalDataManager:NULL
                                                passwordStore:nullptr
diff --git a/ios/chrome/browser/net/ios_chrome_network_delegate.cc b/ios/chrome/browser/net/ios_chrome_network_delegate.cc
index 14395fe..2091ebf 100644
--- a/ios/chrome/browser/net/ios_chrome_network_delegate.cc
+++ b/ios/chrome/browser/net/ios_chrome_network_delegate.cc
@@ -24,6 +24,7 @@
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/cookies/cookie_options.h"
+#include "net/cookies/same_party_context.h"
 #include "net/http/http_status_code.h"
 #include "net/url_request/url_request.h"
 
@@ -111,8 +112,7 @@
     const GURL& url,
     const net::SiteForCookies& site_for_cookies,
     const absl::optional<url::Origin>& top_frame_origin,
-    net::CookieOptions::SamePartyCookieContextType
-        same_party_cookie_context_type) const {
+    net::SamePartyContext::Type same_party_context_type) const {
   // Null during tests, or when we're running in the system context.
   if (!cookie_settings_.get())
     return false;
diff --git a/ios/chrome/browser/net/ios_chrome_network_delegate.h b/ios/chrome/browser/net/ios_chrome_network_delegate.h
index 748da9c..39364499 100644
--- a/ios/chrome/browser/net/ios_chrome_network_delegate.h
+++ b/ios/chrome/browser/net/ios_chrome_network_delegate.h
@@ -12,6 +12,7 @@
 #include "base/memory/ref_counted.h"
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "net/base/network_delegate_impl.h"
+#include "net/cookies/same_party_context.h"
 
 class PrefService;
 
@@ -58,11 +59,11 @@
                       const net::CanonicalCookie& cookie,
                       net::CookieOptions* options,
                       bool allowed_from_caller) override;
-  bool OnForcePrivacyMode(const GURL& url,
-                          const net::SiteForCookies& site_for_cookies,
-                          const absl::optional<url::Origin>& top_frame_origin,
-                          net::CookieOptions::SamePartyCookieContextType
-                              same_party_cookie_context_type) const override;
+  bool OnForcePrivacyMode(
+      const GURL& url,
+      const net::SiteForCookies& site_for_cookies,
+      const absl::optional<url::Origin>& top_frame_origin,
+      net::SamePartyContext::Type same_party_context_type) const override;
   bool OnCancelURLRequestWithPolicyViolatingReferrerHeader(
       const net::URLRequest& request,
       const GURL& target_url,
diff --git a/ios/chrome/browser/passwords/password_controller_unittest.mm b/ios/chrome/browser/passwords/password_controller_unittest.mm
index d3e879c..8b72ac0 100644
--- a/ios/chrome/browser/passwords/password_controller_unittest.mm
+++ b/ios/chrome/browser/passwords/password_controller_unittest.mm
@@ -268,7 +268,7 @@
                  providers:@[ [passwordController_ suggestionProvider] ]];
       accessoryMediator_ =
           [[FormInputAccessoryMediator alloc] initWithConsumer:nil
-                                                      delegate:nil
+                                                       handler:nil
                                                   webStateList:nullptr
                                            personalDataManager:nullptr
                                                  passwordStore:nullptr
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_coordinator.mm b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_coordinator.mm
index 7c2f663..6a7e81c 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_coordinator.mm
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_coordinator.mm
@@ -50,7 +50,7 @@
 @interface FormInputAccessoryCoordinator () <
     AddressCoordinatorDelegate,
     CardCoordinatorDelegate,
-    FormInputAccessoryMediatorDelegate,
+    FormInputAccessoryMediatorHandler,
     ManualFillAccessoryViewControllerDelegate,
     PasswordCoordinatorDelegate,
     SecurityAlertCommands>
@@ -120,7 +120,7 @@
       self.browser->GetCommandDispatcher(), SecurityAlertCommands);
   self.formInputAccessoryMediator = [[FormInputAccessoryMediator alloc]
             initWithConsumer:self.formInputAccessoryViewController
-                    delegate:self
+                     handler:self
                 webStateList:self.browser->GetWebStateList()
          personalDataManager:personalDataManager
                passwordStore:passwordStore
@@ -214,7 +214,7 @@
   [self.childCoordinators addObject:addressCoordinator];
 }
 
-#pragma mark - FormInputAccessoryMediatorDelegate
+#pragma mark - FormInputAccessoryMediatorHandler
 
 - (void)mediatorDidDetectKeyboardHide:(FormInputAccessoryMediator*)mediator {
   // On iOS 13, beta 3, the popover is not dismissed when the keyboard hides.
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.h b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.h
index d15786b..7f73498a7 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.h
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.h
@@ -31,8 +31,8 @@
 
 class WebStateList;
 
-// Delegate in charge of reacting to accessory mediator events.
-@protocol FormInputAccessoryMediatorDelegate
+// Handler in charge of accessory mediator events.
+@protocol FormInputAccessoryMediatorHandler
 
 // The mediator detected that the keyboard was hidden and it is no longer
 // present on the screen.
@@ -54,7 +54,7 @@
 // the passed consumer. `webSateList` can be nullptr and `consumer` can be nil.
 - (instancetype)
           initWithConsumer:(id<FormInputAccessoryConsumer>)consumer
-                  delegate:(id<FormInputAccessoryMediatorDelegate>)delegate
+                   handler:(id<FormInputAccessoryMediatorHandler>)handler
               webStateList:(WebStateList*)webStateList
        personalDataManager:(autofill::PersonalDataManager*)personalDataManager
              passwordStore:
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.mm b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.mm
index 5879bee..f312d75 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.mm
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.mm
@@ -60,8 +60,8 @@
 // The main consumer for this mediator.
 @property(nonatomic, weak) id<FormInputAccessoryConsumer> consumer;
 
-// The delegate for this object.
-@property(nonatomic, weak) id<FormInputAccessoryMediatorDelegate> delegate;
+// The handler for this object.
+@property(nonatomic, weak) id<FormInputAccessoryMediatorHandler> handler;
 
 // The object that manages the currently-shown custom accessory view.
 @property(nonatomic, weak) id<FormInputSuggestionsProvider> currentProvider;
@@ -71,7 +71,7 @@
 
 // The form input handler. This is in charge of form navigation.
 @property(nonatomic, strong)
-    FormInputAccessoryViewHandler* formInputAccessoryHandler;
+    FormInputAccessoryViewHandler* formNavigationHandler;
 
 // The observer to determine when the keyboard dissapears and when it stays.
 @property(nonatomic, strong) KeyboardObserverHelper* keyboardObserver;
@@ -140,7 +140,7 @@
 
 - (instancetype)
           initWithConsumer:(id<FormInputAccessoryConsumer>)consumer
-                  delegate:(id<FormInputAccessoryMediatorDelegate>)delegate
+                   handler:(id<FormInputAccessoryMediatorHandler>)handler
               webStateList:(WebStateList*)webStateList
        personalDataManager:(autofill::PersonalDataManager*)personalDataManager
              passwordStore:
@@ -152,7 +152,7 @@
   if (self) {
     _consumer = consumer;
     _consumer.navigationDelegate = self;
-    _delegate = delegate;
+    _handler = handler;
     if (webStateList) {
       _webStateList = webStateList;
       _webStateListObserver =
@@ -174,8 +174,8 @@
         webState->AddObserver(_webStateObserverBridge.get());
       }
     }
-    _formInputAccessoryHandler = [[FormInputAccessoryViewHandler alloc] init];
-    _formInputAccessoryHandler.webState = _webState;
+    _formNavigationHandler = [[FormInputAccessoryViewHandler alloc] init];
+    _formNavigationHandler.webState = _webState;
 
     NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
     [defaultCenter addObserver:self
@@ -287,7 +287,7 @@
   }
   [self.consumer keyboardWillChangeToState:keyboardState];
   if (!keyboardState.isVisible) {
-    [self.delegate mediatorDidDetectKeyboardHide:self];
+    [self.handler mediatorDidDetectKeyboardHide:self];
   }
 }
 
@@ -333,7 +333,7 @@
   }
   DCHECK(frameID.length);
 
-  [self.formInputAccessoryHandler setLastFocusFormActivityWebFrameID:frameID];
+  [self.formNavigationHandler setLastFocusFormActivityWebFrameID:frameID];
   [self synchronizeNavigationControls];
 
   // Don't look for suggestions in the next events.
@@ -350,17 +350,17 @@
 #pragma mark - FormInputAccessoryViewDelegate
 
 - (void)formInputAccessoryViewDidTapNextButton:(FormInputAccessoryView*)sender {
-  [self.formInputAccessoryHandler selectNextElementWithButtonPress];
+  [self.formNavigationHandler selectNextElementWithButtonPress];
 }
 
 - (void)formInputAccessoryViewDidTapPreviousButton:
     (FormInputAccessoryView*)sender {
-  [self.formInputAccessoryHandler selectPreviousElementWithButtonPress];
+  [self.formNavigationHandler selectPreviousElementWithButtonPress];
 }
 
 - (void)formInputAccessoryViewDidTapCloseButton:
     (FormInputAccessoryView*)sender {
-  [self.formInputAccessoryHandler closeKeyboardWithButtonPress];
+  [self.formNavigationHandler closeKeyboardWithButtonPress];
 }
 
 #pragma mark - CRWWebStateObserver
@@ -416,7 +416,7 @@
   }
   [_currentProvider inputAccessoryViewControllerDidReset];
   _currentProvider = currentProvider;
-  _currentProvider.formInputNavigator = self.formInputAccessoryHandler;
+  _currentProvider.formInputNavigator = self.formNavigationHandler;
 }
 
 #pragma mark - Private
@@ -457,7 +457,7 @@
 // handler state.
 - (void)synchronizeNavigationControls {
   __weak __typeof(self) weakSelf = self;
-  [self.formInputAccessoryHandler
+  [self.formNavigationHandler
       fetchPreviousAndNextElementsPresenceWithCompletionHandler:^(
           bool previousButtonEnabled, bool nextButtonEnabled) {
         weakSelf.consumer.formInputNextButtonEnabled = nextButtonEnabled;
@@ -483,7 +483,7 @@
     if (tabHelper) {
       self.provider = tabHelper->GetAccessoryViewProvider();
     }
-    _formInputAccessoryHandler.webState = webState;
+    _formNavigationHandler.webState = webState;
   } else {
     self.webState = nullptr;
     self.provider = nil;
@@ -497,7 +497,7 @@
   _hasLastSeenParams = NO;
 
   [self.consumer restoreOriginalKeyboardView];
-  [self.formInputAccessoryHandler reset];
+  [self.formNavigationHandler reset];
 
   self.suggestionsDisabled = NO;
   self.currentProvider = nil;
@@ -547,9 +547,9 @@
   }
 }
 
-// Inform the delegate that the app went to the background.
+// Handle applicationDidEnterBackground NSNotification.
 - (void)applicationDidEnterBackground:(NSNotification*)notification {
-  [self.delegate mediatorDidDetectMovingToBackground:self];
+  [self.handler mediatorDidDetectMovingToBackground:self];
 }
 
 - (void)windowDidBecomeKey:(NSNotification*)notification {
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
index 1076e97..42ea377 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
@@ -464,32 +464,26 @@
 + (void)closeAllExtraWindows {
   if (!base::ios::IsMultipleScenesSupported())
     return;
-
-  if (@available(iOS 13, *)) {
-    NSSet<UISceneSession*>* sessions =
-        UIApplication.sharedApplication.openSessions;
-    if (sessions.count <= 1)
-      return;
-    BOOL foundForegroundScene = NO;
-    for (UISceneSession* session in sessions) {
-      UIScene* scene = session.scene;
-      if (!foundForegroundScene && scene &&
-          (scene.activationState == UISceneActivationStateForegroundActive ||
-           scene.activationState == UISceneActivationStateForegroundInactive)) {
-        foundForegroundScene = YES;
-        // Leave the first foreground scene connected, so there's one open
-        // window left.
-        continue;
-      }
-      // If this isn't the first foreground scene, destroy it.
-      UIWindowSceneDestructionRequestOptions* options =
-          [[UIWindowSceneDestructionRequestOptions alloc] init];
-      options.windowDismissalAnimation =
-          UIWindowSceneDismissalAnimationStandard;
-      [UIApplication.sharedApplication requestSceneSessionDestruction:session
-                                                              options:options
-                                                         errorHandler:nil];
+  SceneState* foreground_scene_state =
+      chrome_test_util::GetMainController().appState.foregroundActiveScene;
+  // New windows get an accessibilityIdentifier equal to the number of windows
+  // when they are created.
+  // Renumber the remaining window to avoid conflicts with future windows.
+  foreground_scene_state.window.accessibilityIdentifier = @"0";
+  NSSet<UISceneSession*>* sessions =
+      UIApplication.sharedApplication.openSessions;
+  if (sessions.count <= 1)
+    return;
+  for (UISceneSession* session in sessions) {
+    if (foreground_scene_state.scene == session.scene) {
+      continue;
     }
+    UIWindowSceneDestructionRequestOptions* options =
+        [[UIWindowSceneDestructionRequestOptions alloc] init];
+    options.windowDismissalAnimation = UIWindowSceneDismissalAnimationStandard;
+    [UIApplication.sharedApplication requestSceneSessionDestruction:session
+                                                            options:options
+                                                       errorHandler:nil];
   }
 }
 
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
index 6732d1d4..2ed3794 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-9b7d5289d1c25e0e12d6c02df55c69904a8539c7
\ No newline at end of file
+af3e86e8910d8ee51bb0992ca4b5caba00527d37
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
index ad5e88ca..d4504fb 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-e8ceeab03e24e536f18255cb6881cdef93092607
\ No newline at end of file
+b1ba1e8ffe8abca359af8355cedbf2eaa3171b62
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
index f28a792..7da63c5 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-cd4dca5a5f3e305c3b3706606f379abb720d4cef
\ No newline at end of file
+402cf98947617c7c4c575944f4a5e7ba41b186fe
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
index 12acfde..97e2db2 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-fdac00d5e828ef11bc0e47f7fa4881108fc41adb
\ No newline at end of file
+7e0415ad74f1d9cda277afb8374a401b7e7dd9e5
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
index d5207d5..6b9ea1b 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-aadecd4d989053c0038523c48e2e2607ab39e65c
\ No newline at end of file
+57270688c47436a06da80dfbffa09769d554983b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
index 303e03354..f9e6d6a 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-18a4ef60d1dca93696149b32cb40446faff6b7cd
\ No newline at end of file
+d1ed17ddb0d36e5c2a7a03205e4c29301e9c327f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
index 649b1d0..c6a5591 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-caebf67ab590d3ddf2863e80cd2f0fe7e54def7a
\ No newline at end of file
+1784a96d5b3475aea50df4dfb5328bd510fb5ec6
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
index 554f389..a8079e6 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-5c4dc8196e266eb7350af03c96a33d7e7772c063
\ No newline at end of file
+e4f3e5beb8838ef9cfbd81323ad237803d3a6c9f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
index b54c79c..46ed2b3 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-1805187e0253baa23270bbbbea3c5dd24270eca3
\ No newline at end of file
+594f9c06929189068e0285e84de7468095482e51
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
index 54fc0c4..37cd8d45 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-edd7915c8370cbff07e1282dd7f7c6ca63fb44b4
\ No newline at end of file
+b73a0a668e8c786685b095d3e37ad787f88462ab
\ No newline at end of file
diff --git a/ios/web/web_state/web_state_observer_inttest.mm b/ios/web/web_state/web_state_observer_inttest.mm
index fd8d47ae..81b4dff 100644
--- a/ios/web/web_state/web_state_observer_inttest.mm
+++ b/ios/web/web_state/web_state_observer_inttest.mm
@@ -10,6 +10,7 @@
 #include "base/files/file_path.h"
 #include "base/ios/ios_util.h"
 #import "base/ios/ns_error_util.h"
+#include "base/mac/foundation_util.h"
 #include "base/path_service.h"
 #include "base/scoped_observation.h"
 #include "base/strings/sys_string_conversions.h"
@@ -2427,6 +2428,19 @@
   // Stop the loading.
   web_state()->Stop();
   ASSERT_TRUE(test::WaitForPageToFinishLoading(web_state()));
+
+  // This test will create an unresponsive WebProcess. WebIntTest::TearDown will
+  // call ClearBrowingData, which can take a very long time with an unresponsive
+  // WebProcess. Work around this problem by force closing WKWebView via a
+  // private API.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundeclared-selector"
+  web::WebStateImpl* web_state_impl =
+      static_cast<web::WebStateImpl*>(web_state());
+  WKWebView* web_view = base::mac::ObjCCast<WKWebView>(
+      web_state_impl->GetWebViewNavigationProxy());
+  [web_view performSelector:@selector(_close)];
+#pragma clang diagnostic pop
 }
 
 // Tests that iframe navigation triggers DidChangeBackForwardState.
diff --git a/ipc/BUILD.gn b/ipc/BUILD.gn
index 99f1721..6aaa87e 100644
--- a/ipc/BUILD.gn
+++ b/ipc/BUILD.gn
@@ -90,7 +90,7 @@
     ":mojom",
     ":native_handle_type_converters",
     ":param_traits",
-    "//base/util/type_safety",
+    "//base",
     "//mojo/public/cpp/base",
     "//mojo/public/cpp/bindings",
     "//mojo/public/cpp/system",
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h
index 4639f767..1d7d99d0 100644
--- a/ipc/ipc_message_utils.h
+++ b/ipc/ipc_message_utils.h
@@ -31,7 +31,7 @@
 #include "base/memory/writable_shared_memory_region.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_util.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "build/build_config.h"
 #include "ipc/ipc_param_traits.h"
 #include "ipc/ipc_sync_message.h"
@@ -1063,8 +1063,8 @@
 // base/util types ParamTraits
 
 template <typename TypeMarker, typename WrappedType, WrappedType kInvalidValue>
-struct ParamTraits<util::IdType<TypeMarker, WrappedType, kInvalidValue>> {
-  using param_type = util::IdType<TypeMarker, WrappedType, kInvalidValue>;
+struct ParamTraits<base::IdType<TypeMarker, WrappedType, kInvalidValue>> {
+  using param_type = base::IdType<TypeMarker, WrappedType, kInvalidValue>;
   static void Write(base::Pickle* m, const param_type& p) {
     WriteParam(m, p.GetUnsafeValue());
   }
diff --git a/media/filters/BUILD.gn b/media/filters/BUILD.gn
index 506995d..7fa2057 100644
--- a/media/filters/BUILD.gn
+++ b/media/filters/BUILD.gn
@@ -84,7 +84,7 @@
   ]
 
   deps = [
-    "//base/util/type_safety:type_safety",
+    "//base",
     "//build:chromeos_buildflags",
     "//cc/base",  # For MathUtil.
     "//media:media_buildflags",
diff --git a/media/renderers/audio_renderer_impl.cc b/media/renderers/audio_renderer_impl.cc
index cdcae8e..445479f9 100644
--- a/media/renderers/audio_renderer_impl.cc
+++ b/media/renderers/audio_renderer_impl.cc
@@ -136,7 +136,7 @@
   lock_.AssertAcquired();
 
   sink_playing_ = true;
-
+  was_unmuted_ = was_unmuted_ || volume_ != 0;
   base::AutoUnlock auto_unlock(lock_);
   if (volume_ || !null_sink_)
     sink_->Play();
@@ -731,7 +731,13 @@
 
 void AudioRendererImpl::SetVolume(float volume) {
   DCHECK(task_runner_->BelongsToCurrentThread());
-  was_unmuted_ = was_unmuted_ || volume != 0;
+
+  // Only consider audio as unmuted if the volume is set to a non-zero value
+  // when the state is kPlaying.
+  if (state_ == kPlaying) {
+    was_unmuted_ = was_unmuted_ || volume != 0;
+  }
+
   if (state_ == kUninitialized || state_ == kInitializing) {
     volume_ = volume;
     return;
diff --git a/media/renderers/audio_renderer_impl.h b/media/renderers/audio_renderer_impl.h
index d9ea713..b201648 100644
--- a/media/renderers/audio_renderer_impl.h
+++ b/media/renderers/audio_renderer_impl.h
@@ -108,6 +108,7 @@
   void OnResume() override;
 
   void SetPlayDelayCBForTesting(PlayDelayCBForTesting cb);
+  bool was_unmuted_for_testing() const { return was_unmuted_; }
 
  private:
   friend class AudioRendererImplTest;
diff --git a/media/renderers/audio_renderer_impl_unittest.cc b/media/renderers/audio_renderer_impl_unittest.cc
index 101fd5f..6a76173 100644
--- a/media/renderers/audio_renderer_impl_unittest.cc
+++ b/media/renderers/audio_renderer_impl_unittest.cc
@@ -1686,4 +1686,44 @@
   EXPECT_EQ(buffer_playback_threshold().value, high_latency_playback_threshold);
 }
 
+TEST_F(AudioRendererImplTest, PlayUnmuted) {
+  // Setting the volume to a non-zero value does not count as unmuted until the
+  // audio is played.
+  EXPECT_EQ(renderer_->was_unmuted_for_testing(), 0);
+  renderer_->SetVolume(1);
+  EXPECT_EQ(renderer_->was_unmuted_for_testing(), 0);
+
+  Initialize();
+  Preroll();
+  StartTicking();
+  EXPECT_EQ(renderer_->was_unmuted_for_testing(), 1);
+}
+
+TEST_F(AudioRendererImplTest, UnmuteWhilePlaying) {
+  ConfigureWithMockSink(hardware_params_);
+  EXPECT_CALL(*mock_sink_, SetVolume(0));
+  renderer_->SetVolume(0);
+  EXPECT_EQ(renderer_->was_unmuted_for_testing(), 0);
+
+  EXPECT_CALL(*mock_sink_, Start());
+  EXPECT_CALL(*mock_sink_, Play());
+  Initialize();
+  Preroll();
+  StartTicking();
+  EXPECT_EQ(renderer_->was_unmuted_for_testing(), 0);
+
+  EXPECT_CALL(*mock_sink_, SetVolume(1));
+  renderer_->SetVolume(1);
+  EXPECT_EQ(renderer_->was_unmuted_for_testing(), 1);
+
+  // Muting should pause the sink.
+  EXPECT_CALL(*mock_sink_, SetVolume(0));
+  EXPECT_CALL(*mock_sink_, Pause());
+  renderer_->SetVolume(0);
+  EXPECT_EQ(renderer_->was_unmuted_for_testing(), 1);
+
+  StopTicking();
+  EXPECT_CALL(*mock_sink_, Stop());
+}
+
 }  // namespace media
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index 42feb7a..13ad53b 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -236,7 +236,6 @@
     ":bindings_base",
     ":struct_traits",
     "//base",
-    "//base/util/type_safety",
     "//ipc:message_support",
     "//ipc:param_traits",
     "//mojo/public/cpp/system",
diff --git a/mojo/public/cpp/bindings/remote_set.h b/mojo/public/cpp/bindings/remote_set.h
index 1453d4a..3426704c 100644
--- a/mojo/public/cpp/bindings/remote_set.h
+++ b/mojo/public/cpp/bindings/remote_set.h
@@ -15,7 +15,7 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/sequenced_task_runner.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -27,7 +27,7 @@
 struct RemoteSetElementIdTypeTag {};
 }  // namespace internal
 
-using RemoteSetElementId = util::IdTypeU32<internal::RemoteSetElementIdTypeTag>;
+using RemoteSetElementId = base::IdTypeU32<internal::RemoteSetElementIdTypeTag>;
 
 // Shared implementation of a set of remotes, used by both RemoteSet and
 // AssociatedRemoteSet aliases (see below).
diff --git a/mojo/public/tools/mojom/mojom_parser.py b/mojo/public/tools/mojom/mojom_parser.py
index 48431e9..07a7ba5 100755
--- a/mojo/public/tools/mojom/mojom_parser.py
+++ b/mojo/public/tools/mojom/mojom_parser.py
@@ -255,6 +255,7 @@
 def _ParseMojoms(mojom_files,
                  input_root_paths,
                  output_root_path,
+                 module_root_paths,
                  enabled_features,
                  module_metadata,
                  allowed_imports=None):
@@ -270,6 +271,8 @@
         are based on the mojom's relative path, rebased onto this path.
         Additionally, the script expects this root to contain already-generated
         modules for any transitive dependencies not listed in mojom_files.
+    module_root_paths: A list of absolute filesystem paths which contain
+        already-generated modules for any non-transitive dependencies.
     enabled_features: A list of enabled feature names, controlling which AST
         nodes are filtered by [EnableIf] attributes.
     module_metadata: A list of 2-tuples representing metadata key-value pairs to
@@ -320,8 +323,8 @@
         # be parsed and have a module file sitting in a corresponding output
         # location.
         module_path = _GetModuleFilename(imp.import_filename)
-        module_abspath = _ResolveRelativeImportPath(module_path,
-                                                    [output_root_path])
+        module_abspath = _ResolveRelativeImportPath(
+            module_path, module_root_paths + [output_root_path])
         with open(module_abspath, 'rb') as module_file:
           loaded_modules[import_abspath] = module.Module.Load(module_file)
 
@@ -396,6 +399,15 @@
       'ROOT is also searched for existing modules of any transitive imports '
       'which were not included in the set of inputs.')
   arg_parser.add_argument(
+      '--module-root',
+      default=[],
+      action='append',
+      metavar='ROOT',
+      dest='module_root_paths',
+      help='Adds ROOT to the set of root paths to search for existing modules '
+      'of non-transitive imports. Provided root paths are always searched in '
+      'order from longest absolute path to shortest.')
+  arg_parser.add_argument(
       '--mojoms',
       nargs='+',
       dest='mojom_files',
@@ -461,6 +473,7 @@
   mojom_files = list(map(os.path.abspath, args.mojom_files))
   input_roots = list(map(os.path.abspath, args.input_root_paths))
   output_root = os.path.abspath(args.output_root_path)
+  module_roots = list(map(os.path.abspath, args.module_root_paths))
 
   if args.build_metadata_filename:
     allowed_imports = _CollectAllowedImportsFromBuildMetadata(
@@ -470,8 +483,8 @@
 
   module_metadata = list(
       map(lambda kvp: tuple(kvp.split('=')), args.module_metadata))
-  _ParseMojoms(mojom_files, input_roots, output_root, args.enabled_features,
-               module_metadata, allowed_imports)
+  _ParseMojoms(mojom_files, input_roots, output_root, module_roots,
+               args.enabled_features, module_metadata, allowed_imports)
   logging.info('Finished')
   # Exit without running GC, which can save multiple seconds due the large
   # number of object created.
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 5857481..028a63b 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -415,6 +415,8 @@
     "cookies/cookie_util.h",
     "cookies/parsed_cookie.cc",
     "cookies/parsed_cookie.h",
+    "cookies/same_party_context.cc",
+    "cookies/same_party_context.h",
     "cookies/site_for_cookies.cc",
     "cookies/site_for_cookies.h",
     "cookies/static_cookie_policy.cc",
@@ -1644,7 +1646,6 @@
     ":preload_decoder",
     "//base",
     "//base/third_party/dynamic_annotations",
-    "//base/util/type_safety:type_safety",
     "//net/base/registry_controlled_domains",
     "//third_party/protobuf:protobuf_lite",
     "//third_party/zlib",
diff --git a/net/base/network_delegate.cc b/net/base/network_delegate.cc
index 6092315..712f3a3 100644
--- a/net/base/network_delegate.cc
+++ b/net/base/network_delegate.cc
@@ -129,12 +129,11 @@
     const GURL& url,
     const SiteForCookies& site_for_cookies,
     const absl::optional<url::Origin>& top_frame_origin,
-    CookieOptions::SamePartyCookieContextType same_party_cookie_context_type)
-    const {
+    SamePartyContext::Type same_party_context_type) const {
   TRACE_EVENT0(NetTracingCategory(), "NetworkDelegate::ForcePrivacyMode");
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return OnForcePrivacyMode(url, site_for_cookies, top_frame_origin,
-                            same_party_cookie_context_type);
+                            same_party_context_type);
 }
 
 bool NetworkDelegate::CancelURLRequestWithPolicyViolatingReferrerHeader(
diff --git a/net/base/network_delegate.h b/net/base/network_delegate.h
index ac54b08..730c07c 100644
--- a/net/base/network_delegate.h
+++ b/net/base/network_delegate.h
@@ -17,6 +17,7 @@
 #include "net/base/net_export.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/cookies/cookie_inclusion_status.h"
+#include "net/cookies/same_party_context.h"
 #include "net/cookies/site_for_cookies.h"
 #include "net/proxy_resolution/proxy_retry_info.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -84,8 +85,7 @@
   bool ForcePrivacyMode(const GURL& url,
                         const SiteForCookies& site_for_cookies,
                         const absl::optional<url::Origin>& top_frame_origin,
-                        CookieOptions::SamePartyCookieContextType
-                            same_party_cookie_context_type) const;
+                        SamePartyContext::Type same_party_context_type) const;
 
   bool CancelURLRequestWithPolicyViolatingReferrerHeader(
       const URLRequest& request,
@@ -249,8 +249,7 @@
       const GURL& url,
       const SiteForCookies& site_for_cookies,
       const absl::optional<url::Origin>& top_frame_origin,
-      CookieOptions::SamePartyCookieContextType same_party_cookie_context_type)
-      const = 0;
+      SamePartyContext::Type same_party_cookie_context_type) const = 0;
 
   // Called when the |referrer_url| for requesting |target_url| during handling
   // of the |request| is does not comply with the referrer policy (e.g. a
diff --git a/net/base/network_delegate_impl.cc b/net/base/network_delegate_impl.cc
index d9cfedc..37ac8ee 100644
--- a/net/base/network_delegate_impl.cc
+++ b/net/base/network_delegate_impl.cc
@@ -5,6 +5,7 @@
 #include "net/base/network_delegate_impl.h"
 
 #include "net/base/net_errors.h"
+#include "net/cookies/same_party_context.h"
 
 namespace net {
 
@@ -69,8 +70,7 @@
     const GURL& url,
     const SiteForCookies& site_for_cookies,
     const absl::optional<url::Origin>& top_frame_origin,
-    CookieOptions::SamePartyCookieContextType same_party_cookie_context_type)
-    const {
+    SamePartyContext::Type same_party_context_type) const {
   return false;
 }
 
diff --git a/net/base/network_delegate_impl.h b/net/base/network_delegate_impl.h
index 2edbcd1..e32015e 100644
--- a/net/base/network_delegate_impl.h
+++ b/net/base/network_delegate_impl.h
@@ -14,6 +14,7 @@
 #include "net/base/net_export.h"
 #include "net/base/network_delegate.h"
 #include "net/cookies/canonical_cookie.h"
+#include "net/cookies/same_party_context.h"
 #include "net/proxy_resolution/proxy_retry_info.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -75,11 +76,11 @@
                       CookieOptions* options,
                       bool allowed_from_caller) override;
 
-  bool OnForcePrivacyMode(const GURL& url,
-                          const SiteForCookies& site_for_cookies,
-                          const absl::optional<url::Origin>& top_frame_origin,
-                          CookieOptions::SamePartyCookieContextType
-                              same_party_cookie_context_type) const override;
+  bool OnForcePrivacyMode(
+      const GURL& url,
+      const SiteForCookies& site_for_cookies,
+      const absl::optional<url::Origin>& top_frame_origin,
+      SamePartyContext::Type same_party_context_type) const override;
 
   bool OnCancelURLRequestWithPolicyViolatingReferrerHeader(
       const URLRequest& request,
diff --git a/net/cookies/canonical_cookie.cc b/net/cookies/canonical_cookie.cc
index 59ad668..32b931c 100644
--- a/net/cookies/canonical_cookie.cc
+++ b/net/cookies/canonical_cookie.cc
@@ -261,6 +261,32 @@
   status->MaybeClearSameSiteWarning();
 }
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class SameSiteNonePartyContextType {
+  // SameSite=None was required in order for the cookie to be included.
+  kSameSiteNoneRequired = 0,
+  // The cookie would have been included if it were SameParty (using only the
+  // top frame and resource URL).
+  kSamePartyTopResource = 1,
+  // The cookie would have been included if it were SameParty (using the
+  // resource URL and all frame ancestors).
+  kSamePartyAncestors = 2,
+  // The cookie would have been included if it were SameSite=Lax.
+  kSameSiteLax = 3,
+  // The cookie would have been included if it were SameSite=Strict.
+  kSameSiteStrict = 4,
+  kMaxValue = kSameSiteStrict
+};
+
+void RecordSameSiteNoneReadContextMetric(SameSiteNonePartyContextType type) {
+  UMA_HISTOGRAM_ENUMERATION("Cookie.SameSiteNone.PartyContext.Read", type);
+}
+
+void RecordSameSiteNoneWriteContextMetric(SameSiteNonePartyContextType type) {
+  UMA_HISTOGRAM_ENUMERATION("Cookie.SameSiteNone.PartyContext.Write", type);
+}
+
 }  // namespace
 
 CookieAccessParams::CookieAccessParams(CookieAccessSemantics access_semantics,
@@ -883,6 +909,47 @@
     UMA_HISTOGRAM_ENUMERATION("Cookie.IncludedRequestEffectiveSameSite",
                               effective_same_site,
                               CookieEffectiveSameSite::COUNT);
+
+    if (SameSite() == CookieSameSite::NO_RESTRICTION) {
+      SamePartyContext::Type top_resource =
+          options.same_party_context().top_resource_for_metrics_only();
+      SamePartyContext::Type ancestors =
+          options.same_party_context().ancestors_for_metrics_only();
+
+      if (top_resource == SamePartyContext::Type::kCrossParty) {
+        status.AddWarningReason(
+            CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED);
+        RecordSameSiteNoneReadContextMetric(
+            SameSiteNonePartyContextType::kSameSiteNoneRequired);
+      } else if (ancestors == SamePartyContext::Type::kCrossParty) {
+        status.AddWarningReason(
+            CookieInclusionStatus::
+                WARN_SAMESITE_NONE_INCLUDED_BY_SAMEPARTY_TOP_RESOURCE);
+        RecordSameSiteNoneReadContextMetric(
+            SameSiteNonePartyContextType::kSamePartyTopResource);
+      } else if (cookie_inclusion_context <
+                 CookieOptions::SameSiteCookieContext::ContextType::
+                     SAME_SITE_LAX) {
+        status.AddWarningReason(
+            CookieInclusionStatus::
+                WARN_SAMESITE_NONE_INCLUDED_BY_SAMEPARTY_ANCESTORS);
+        RecordSameSiteNoneReadContextMetric(
+            SameSiteNonePartyContextType::kSamePartyAncestors);
+      } else if (cookie_inclusion_context <
+                 CookieOptions::SameSiteCookieContext::ContextType::
+                     SAME_SITE_STRICT) {
+        status.AddWarningReason(
+            CookieInclusionStatus::WARN_SAMESITE_NONE_INCLUDED_BY_SAMESITE_LAX);
+        RecordSameSiteNoneReadContextMetric(
+            SameSiteNonePartyContextType::kSameSiteLax);
+      } else {
+        status.AddWarningReason(
+            CookieInclusionStatus::
+                WARN_SAMESITE_NONE_INCLUDED_BY_SAMESITE_STRICT);
+        RecordSameSiteNoneReadContextMetric(
+            SameSiteNonePartyContextType::kSameSiteStrict);
+      }
+    }
   }
 
   if (status.ShouldRecordDowngradeMetrics()) {
@@ -890,7 +957,6 @@
                               status.GetBreakingDowngradeMetricsEnumValue(url));
   }
 
-  // TODO(chlily): Log metrics.
   return CookieAccessResult(effective_same_site, status,
                             params.access_semantics,
                             is_allowed_to_access_secure_cookies);
@@ -1072,9 +1138,43 @@
     UMA_HISTOGRAM_ENUMERATION("Cookie.IncludedResponseEffectiveSameSite",
                               access_result.effective_same_site,
                               CookieEffectiveSameSite::COUNT);
-  }
 
-  // TODO(chlily): Log metrics.
+    if (SameSite() == CookieSameSite::NO_RESTRICTION) {
+      SamePartyContext::Type top_resource =
+          options.same_party_context().top_resource_for_metrics_only();
+      SamePartyContext::Type ancestors =
+          options.same_party_context().ancestors_for_metrics_only();
+
+      if (top_resource == SamePartyContext::Type::kCrossParty) {
+        access_result.status.AddWarningReason(
+            CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED);
+        RecordSameSiteNoneWriteContextMetric(
+            SameSiteNonePartyContextType::kSameSiteNoneRequired);
+      } else if (ancestors == SamePartyContext::Type::kCrossParty) {
+        access_result.status.AddWarningReason(
+            CookieInclusionStatus::
+                WARN_SAMESITE_NONE_INCLUDED_BY_SAMEPARTY_TOP_RESOURCE);
+        RecordSameSiteNoneWriteContextMetric(
+            SameSiteNonePartyContextType::kSamePartyTopResource);
+      } else if (cookie_inclusion_context <
+                 CookieOptions::SameSiteCookieContext::ContextType::
+                     SAME_SITE_LAX) {
+        access_result.status.AddWarningReason(
+            CookieInclusionStatus::
+                WARN_SAMESITE_NONE_INCLUDED_BY_SAMEPARTY_ANCESTORS);
+        RecordSameSiteNoneWriteContextMetric(
+            SameSiteNonePartyContextType::kSamePartyAncestors);
+      } else {
+        // NB: unlike when sending a cookie, there's no distinction between
+        // SameSite=Lax and SameSite=Strict when setting a cookie.
+        access_result.status.AddWarningReason(
+            CookieInclusionStatus::
+                WARN_SAMESITE_NONE_INCLUDED_BY_SAMESITE_STRICT);
+        RecordSameSiteNoneWriteContextMetric(
+            SameSiteNonePartyContextType::kSameSiteStrict);
+      }
+    }
+  }
 
   return access_result;
 }
diff --git a/net/cookies/canonical_cookie_test_helpers.h b/net/cookies/canonical_cookie_test_helpers.h
index d02f3f6..ab9908e 100644
--- a/net/cookies/canonical_cookie_test_helpers.h
+++ b/net/cookies/canonical_cookie_test_helpers.h
@@ -99,6 +99,15 @@
       result_listener);
 }
 
+// Helper for checking that status.HasExactlyWarningReasonsForTesting(reasons)
+// == true.
+MATCHER_P(HasExactlyWarningReasonsForTesting, reasons, "") {
+  const CookieInclusionStatus status = arg;
+  return testing::ExplainMatchResult(
+      true, status.HasExactlyWarningReasonsForTesting(reasons),
+      result_listener);
+}
+
 MATCHER(ShouldWarn, "") {
   net::CookieInclusionStatus status = arg;
   return testing::ExplainMatchResult(true, status.ShouldWarn(),
diff --git a/net/cookies/canonical_cookie_unittest.cc b/net/cookies/canonical_cookie_unittest.cc
index a07e60f..f8a74479 100644
--- a/net/cookies/canonical_cookie_unittest.cc
+++ b/net/cookies/canonical_cookie_unittest.cc
@@ -1095,7 +1095,8 @@
                                CookieSamePartyStatus::kNoSamePartyEnforcement}),
         MatchesCookieAccessResult(test.expected_inclusion_status,
                                   test.expected_effective_samesite,
-                                  access_semantics, true));
+                                  access_semantics, true))
+        << cookie->Name() << "=" << cookie->Value();
   }
 }
 
@@ -1158,21 +1159,25 @@
       {"Common=10;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION,
        CookieEffectiveSameSite::NO_RESTRICTION,
        SameSiteCookieContext(SameSiteCookieContext::ContextType::CROSS_SITE),
-       CookieInclusionStatus()},
+       CookieInclusionStatus(
+           CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED)},
       {"Common=11;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION,
        CookieEffectiveSameSite::NO_RESTRICTION,
        SameSiteCookieContext(
            SameSiteCookieContext::ContextType::SAME_SITE_LAX_METHOD_UNSAFE),
-       CookieInclusionStatus()},
+       CookieInclusionStatus(
+           CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED)},
       {"Common=12;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION,
        CookieEffectiveSameSite::NO_RESTRICTION,
        SameSiteCookieContext(SameSiteCookieContext::ContextType::SAME_SITE_LAX),
-       CookieInclusionStatus()},
+       CookieInclusionStatus(
+           CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED)},
       {"Common=13;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION,
        CookieEffectiveSameSite::NO_RESTRICTION,
        SameSiteCookieContext(
            SameSiteCookieContext::ContextType::SAME_SITE_STRICT),
-       CookieInclusionStatus()},
+       CookieInclusionStatus(
+           CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED)},
       // Because NO_RESTRICTION cookies are always sent, the schemeful context
       // downgrades shouldn't matter.
       {"Common=14;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION,
@@ -1180,30 +1185,35 @@
        SameSiteCookieContext(
            SameSiteCookieContext::ContextType::SAME_SITE_STRICT,
            SameSiteCookieContext::ContextType::SAME_SITE_LAX),
-       CookieInclusionStatus()},
+       CookieInclusionStatus(
+           CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED)},
       {"Common=15;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION,
        CookieEffectiveSameSite::NO_RESTRICTION,
        SameSiteCookieContext(
            SameSiteCookieContext::ContextType::SAME_SITE_STRICT,
            SameSiteCookieContext::ContextType::SAME_SITE_LAX_METHOD_UNSAFE),
-       CookieInclusionStatus()},
+       CookieInclusionStatus(
+           CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED)},
       {"Common=16;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION,
        CookieEffectiveSameSite::NO_RESTRICTION,
        SameSiteCookieContext(
            SameSiteCookieContext::ContextType::SAME_SITE_STRICT,
            SameSiteCookieContext::ContextType::CROSS_SITE),
-       CookieInclusionStatus()},
+       CookieInclusionStatus(
+           CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED)},
       {"Common=17;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION,
        CookieEffectiveSameSite::NO_RESTRICTION,
        SameSiteCookieContext(SameSiteCookieContext::ContextType::SAME_SITE_LAX,
                              SameSiteCookieContext::ContextType::CROSS_SITE),
-       CookieInclusionStatus()},
+       CookieInclusionStatus(
+           CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED)},
       {"Common=18;SameSite=None;Secure", CookieSameSite::NO_RESTRICTION,
        CookieEffectiveSameSite::NO_RESTRICTION,
        SameSiteCookieContext(
            SameSiteCookieContext::ContextType::SAME_SITE_LAX_METHOD_UNSAFE,
            SameSiteCookieContext::ContextType::CROSS_SITE),
-       CookieInclusionStatus()},
+       CookieInclusionStatus(
+           CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED)},
   };
 
   // Test cases where the default is None (either access semantics is LEGACY, or
@@ -1225,6 +1235,7 @@
            std::vector<CookieInclusionStatus::ExclusionReason>(),
            {CookieInclusionStatus::
                 WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT})},
+
       {"DefaultNone=3", CookieSameSite::UNSPECIFIED,
        CookieEffectiveSameSite::NO_RESTRICTION,
        SameSiteCookieContext(SameSiteCookieContext::ContextType::SAME_SITE_LAX),
@@ -1624,6 +1635,168 @@
   }
 }
 
+TEST(CanonicalCookieTest, IncludeForRequestURL_SameSiteNone_Metrics) {
+  using SamePartyContextType = SamePartyContext::Type;
+
+  constexpr bool delegate_treats_url_as_trustworthy = false;
+  const base::Time now = base::Time::Now();
+  const auto make_cookie = [now](CookieSameSite same_site, bool same_party) {
+    return CanonicalCookie::CreateUnsafeCookieForTesting(
+        "A", "1", "www.example.com", "/test", now, base::Time(), base::Time(),
+        true /* secure */, false /*httponly*/, same_site,
+        COOKIE_PRIORITY_DEFAULT, same_party);
+  };
+  GURL url("https://www.example.com/test");
+
+  const std::unique_ptr<CanonicalCookie> same_site_none_cookie =
+      make_cookie(CookieSameSite::NO_RESTRICTION, false /* same_party */);
+  const std::unique_ptr<CanonicalCookie> same_party_cookie =
+      make_cookie(CookieSameSite::NO_RESTRICTION, true /* same_party */);
+  const std::unique_ptr<CanonicalCookie> same_site_lax_cookie =
+      make_cookie(CookieSameSite::LAX_MODE, false /* same_party */);
+  const std::unique_ptr<CanonicalCookie> same_site_strict_cookie =
+      make_cookie(CookieSameSite::STRICT_MODE, false /* same_party */);
+  CookieOptions options;
+  options.set_same_site_cookie_context(CookieOptions::SameSiteCookieContext(
+      CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE));
+  // Same as default, but just to be explicit:
+  options.set_same_party_context(
+      SamePartyContext(SamePartyContext::Type::kCrossParty));
+
+  // Check that the most restrictive context is recognized and enforced.
+  EXPECT_THAT(
+      same_site_none_cookie->IncludeForRequestURL(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement)),
+      MatchesCookieAccessResult(
+          CookieInclusionStatus::MakeFromReasonsForTesting(
+              {}, {CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED}),
+          _, _, true));
+  EXPECT_THAT(
+      same_party_cookie->IncludeForRequestURL(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kEnforceSamePartyExclude)),
+      MatchesCookieAccessResult(Not(net::IsInclude()), _, _, true));
+
+  // Now tweak the context to allow a SameParty cookie (using the top&resource
+  // definition) and make sure we get the right warning. (Note: we make the
+  // "real" same-partyness value match the value computed for the metric, even
+  // though they would differ unless we changed the real definition.) Then
+  // check that if we modify the cookie as indicated, the set would be allowed,
+  // but the next-most-restrictive variation would still be blocked.
+  options.set_same_party_context(SamePartyContext(
+      SamePartyContextType::kSameParty, SamePartyContextType::kCrossParty,
+      SamePartyContextType::kSameParty));
+  EXPECT_THAT(
+      same_site_none_cookie->IncludeForRequestURL(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement)),
+      MatchesCookieAccessResult(
+          CookieInclusionStatus::MakeFromReasonsForTesting(
+              {}, {CookieInclusionStatus::
+                       WARN_SAMESITE_NONE_INCLUDED_BY_SAMEPARTY_TOP_RESOURCE}),
+          _, _, true));
+  EXPECT_THAT(
+      same_party_cookie->IncludeForRequestURL(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kEnforceSamePartyInclude)),
+      MatchesCookieAccessResult(net::IsInclude(), _, _, true));
+  EXPECT_THAT(
+      same_site_lax_cookie->IncludeForRequestURL(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement)),
+      MatchesCookieAccessResult(Not(net::IsInclude()), _, _, true));
+
+  // Next: allow a SameParty cookie (using both definitions).
+  options.set_same_party_context(SamePartyContext::MakeInclusive());
+  EXPECT_THAT(
+      same_site_none_cookie->IncludeForRequestURL(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement)),
+      MatchesCookieAccessResult(
+          CookieInclusionStatus::MakeFromReasonsForTesting(
+              {}, {CookieInclusionStatus::
+                       WARN_SAMESITE_NONE_INCLUDED_BY_SAMEPARTY_ANCESTORS}),
+          _, _, true));
+  EXPECT_THAT(
+      same_party_cookie->IncludeForRequestURL(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement)),
+      MatchesCookieAccessResult(net::IsInclude(), _, _, true));
+  EXPECT_THAT(
+      same_site_lax_cookie->IncludeForRequestURL(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement)),
+      MatchesCookieAccessResult(Not(net::IsInclude()), _, _, true));
+
+  // Next: allow a SameSite=Lax cookie.
+  options.set_same_site_cookie_context(CookieOptions::SameSiteCookieContext(
+      CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX));
+  EXPECT_THAT(
+      same_site_none_cookie->IncludeForRequestURL(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement)),
+      MatchesCookieAccessResult(
+          CookieInclusionStatus::MakeFromReasonsForTesting(
+              {}, {CookieInclusionStatus::
+                       WARN_SAMESITE_NONE_INCLUDED_BY_SAMESITE_LAX}),
+          _, _, true));
+  EXPECT_THAT(
+      same_site_lax_cookie->IncludeForRequestURL(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement)),
+      MatchesCookieAccessResult(net::IsInclude(), _, _, true));
+  EXPECT_THAT(
+      same_site_strict_cookie->IncludeForRequestURL(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement)),
+      MatchesCookieAccessResult(Not(net::IsInclude()), _, _, true));
+
+  // Next: allow a SameSite=Strict cookie.
+  options.set_same_site_cookie_context(CookieOptions::SameSiteCookieContext(
+      CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT));
+  EXPECT_THAT(
+      same_site_none_cookie->IncludeForRequestURL(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement)),
+      MatchesCookieAccessResult(
+          CookieInclusionStatus::MakeFromReasonsForTesting(
+              {}, {CookieInclusionStatus::
+                       WARN_SAMESITE_NONE_INCLUDED_BY_SAMESITE_STRICT}),
+          _, _, true));
+  EXPECT_THAT(
+      same_site_strict_cookie->IncludeForRequestURL(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement)),
+      MatchesCookieAccessResult(net::IsInclude(), _, _, true));
+}
+
 TEST(CanonicalCookieTest, MultipleExclusionReasons) {
   GURL url("http://www.not-secure.com/foo");
   base::Time creation_time = base::Time::Now();
@@ -3680,14 +3853,163 @@
   }
 }
 
+TEST(CanonicalCookieTest, IsSetPermitted_SameSiteNone_Metrics) {
+  using SamePartyContextType = SamePartyContext::Type;
+
+  constexpr bool delegate_treats_url_as_trustworthy = false;
+  const base::Time now = base::Time::Now();
+  const auto make_cookie = [now](CookieSameSite same_site, bool same_party) {
+    return CanonicalCookie::CreateUnsafeCookieForTesting(
+        "A", "1", "www.example.com", "/test", now, base::Time(), base::Time(),
+        true /* secure */, false /*httponly*/, same_site,
+        COOKIE_PRIORITY_DEFAULT, same_party);
+  };
+  GURL url("https://www.example.com/test");
+
+  const std::unique_ptr<CanonicalCookie> same_site_none_cookie =
+      make_cookie(CookieSameSite::NO_RESTRICTION, false /* same_party */);
+  const std::unique_ptr<CanonicalCookie> same_party_cookie =
+      make_cookie(CookieSameSite::NO_RESTRICTION, true /* same_party */);
+  const std::unique_ptr<CanonicalCookie> same_site_lax_cookie =
+      make_cookie(CookieSameSite::LAX_MODE, false /* same_party */);
+  const std::unique_ptr<CanonicalCookie> same_site_strict_cookie =
+      make_cookie(CookieSameSite::STRICT_MODE, false /* same_party */);
+  CookieOptions options;
+
+  options.set_same_site_cookie_context(CookieOptions::SameSiteCookieContext(
+      CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE));
+  // Same as default, but just to be explicit:
+  options.set_same_party_context(
+      SamePartyContext(SamePartyContext::Type::kCrossParty));
+  EXPECT_THAT(
+      same_site_none_cookie->IsSetPermittedInContext(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement),
+          kCookieableSchemes),
+      MatchesCookieAccessResult(
+          CookieInclusionStatus::MakeFromReasonsForTesting(
+              {}, {CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED}),
+          _, _, true));
+  EXPECT_THAT(
+      same_party_cookie->IsSetPermittedInContext(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kEnforceSamePartyExclude),
+          kCookieableSchemes),
+      MatchesCookieAccessResult(Not(net::IsInclude()), _, _, true));
+
+  // Now tweak the context to allow a SameParty cookie (using the top&resource
+  // definition) and make sure we get the right warning. (Note: we make the
+  // "real" same-partyness value match the value computed for the metric, even
+  // though they would differ unless we changed the real definition.) Then
+  // check that if we modify the cookie as indicated, the set would be allowed.
+  options.set_same_party_context(SamePartyContext(
+      SamePartyContextType::kSameParty, SamePartyContextType::kCrossParty,
+      SamePartyContextType::kSameParty));
+  EXPECT_THAT(
+      same_site_none_cookie->IsSetPermittedInContext(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement),
+          kCookieableSchemes),
+      MatchesCookieAccessResult(
+          CookieInclusionStatus::MakeFromReasonsForTesting(
+              {}, {CookieInclusionStatus::
+                       WARN_SAMESITE_NONE_INCLUDED_BY_SAMEPARTY_TOP_RESOURCE}),
+          _, _, true));
+  EXPECT_THAT(
+      same_party_cookie->IsSetPermittedInContext(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kEnforceSamePartyInclude),
+          kCookieableSchemes),
+      MatchesCookieAccessResult(net::IsInclude(), _, _, true));
+  EXPECT_THAT(
+      same_site_lax_cookie->IsSetPermittedInContext(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement),
+          kCookieableSchemes),
+      MatchesCookieAccessResult(Not(net::IsInclude()), _, _, true));
+
+  // Next: allow a SameParty cookie (using both definitions).
+  options.set_same_party_context(SamePartyContext::MakeInclusive());
+  EXPECT_THAT(
+      same_site_none_cookie->IsSetPermittedInContext(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement),
+          kCookieableSchemes),
+      MatchesCookieAccessResult(
+          CookieInclusionStatus::MakeFromReasonsForTesting(
+              {}, {CookieInclusionStatus::
+                       WARN_SAMESITE_NONE_INCLUDED_BY_SAMEPARTY_ANCESTORS}),
+          _, _, true));
+  EXPECT_THAT(
+      same_party_cookie->IsSetPermittedInContext(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement),
+          kCookieableSchemes),
+      MatchesCookieAccessResult(net::IsInclude(), _, _, true));
+  EXPECT_THAT(
+      same_site_lax_cookie->IsSetPermittedInContext(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement),
+          kCookieableSchemes),
+      MatchesCookieAccessResult(Not(net::IsInclude()), _, _, true));
+
+  // Next: allow a SameSite=Lax or SameSite=Strict cookie.
+  options.set_same_site_cookie_context(CookieOptions::SameSiteCookieContext(
+      CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX));
+  EXPECT_THAT(
+      same_site_none_cookie->IsSetPermittedInContext(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement),
+          kCookieableSchemes),
+      MatchesCookieAccessResult(
+          CookieInclusionStatus::MakeFromReasonsForTesting(
+              {}, {CookieInclusionStatus::
+                       WARN_SAMESITE_NONE_INCLUDED_BY_SAMESITE_STRICT}),
+          _, _, true));
+  EXPECT_THAT(
+      same_site_lax_cookie->IsSetPermittedInContext(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement),
+          kCookieableSchemes),
+      MatchesCookieAccessResult(net::IsInclude(), _, _, true));
+  EXPECT_THAT(
+      same_site_strict_cookie->IsSetPermittedInContext(
+          url, options,
+          CookieAccessParams(CookieAccessSemantics::LEGACY,
+                             delegate_treats_url_as_trustworthy,
+                             CookieSamePartyStatus::kNoSamePartyEnforcement),
+          kCookieableSchemes),
+      MatchesCookieAccessResult(net::IsInclude(), _, _, true));
+}
+
 TEST(CanonicalCookieTest, IsSetPermitted_SameParty) {
   GURL url("https://www.example.com/test");
   base::Time current_time = base::Time::Now();
   CookieOptions options;
   options.set_same_site_cookie_context(CookieOptions::SameSiteCookieContext(
       CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE));
-  options.set_same_party_cookie_context_type(
-      CookieOptions::SamePartyCookieContextType::kSameParty);
+  options.set_same_party_context(
+      SamePartyContext(SamePartyContext::Type::kSameParty));
 
   {
     bool delegate_treats_url_as_trustworthy = false;
diff --git a/net/cookies/cookie_access_delegate.h b/net/cookies/cookie_access_delegate.h
index 88a96f1..2c5715e 100644
--- a/net/cookies/cookie_access_delegate.h
+++ b/net/cookies/cookie_access_delegate.h
@@ -9,6 +9,7 @@
 #include "net/base/net_export.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/cookies/cookie_constants.h"
+#include "net/cookies/same_party_context.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
@@ -39,12 +40,12 @@
       const GURL& url,
       const SiteForCookies& site_for_cookies) const = 0;
 
-  // Returns whether `site` is same-party with `party_context` and
-  // `top_frame_site`. If `top_frame_site` is nullopt, then `site` will be
-  // checked only against `party_context`.
-  virtual bool IsContextSamePartyWithSite(
+  // Returns the SamePartyContext indicating whether `site` is same-party
+  // with `party_context` and `top_frame_site`. If `top_frame_site` is nullptr,
+  // then `site` will be checked only against `party_context`.
+  virtual SamePartyContext ComputeSamePartyContext(
       const net::SchemefulSite& site,
-      const absl::optional<net::SchemefulSite>& top_frame_site,
+      const net::SchemefulSite* top_frame_site,
       const std::set<net::SchemefulSite>& party_context) const = 0;
 
   // Returns whether `site` belongs to a non-singleton First-Party Set.
diff --git a/net/cookies/cookie_inclusion_status.cc b/net/cookies/cookie_inclusion_status.cc
index b9c2160..9e269c1 100644
--- a/net/cookies/cookie_inclusion_status.cc
+++ b/net/cookies/cookie_inclusion_status.cc
@@ -31,6 +31,9 @@
     : exclusion_reasons_(GetExclusionBitmask(reason)),
       warning_reasons_(GetWarningBitmask(warning)) {}
 
+CookieInclusionStatus::CookieInclusionStatus(WarningReason warning)
+    : warning_reasons_(GetWarningBitmask(warning)) {}
+
 bool CookieInclusionStatus::operator==(
     const CookieInclusionStatus& other) const {
   return exclusion_reasons_ == other.exclusion_reasons_ &&
@@ -259,6 +262,15 @@
             "WARN_SAMEPARTY_INCLUSION_OVERRULED_SAMESITE"},
            {WARN_SAMESITE_LAX_EXCLUDED_AFTER_BUGFIX_1166211,
             "WARN_SAMESITE_LAX_EXCLUDED_AFTER_BUGFIX_1166211"},
+           {WARN_SAMESITE_NONE_REQUIRED, "WARN_SAMESITE_NONE_REQUIRED"},
+           {WARN_SAMESITE_NONE_INCLUDED_BY_SAMEPARTY_TOP_RESOURCE,
+            "WARN_SAMESITE_NONE_INCLUDED_BY_SAMEPARTY_TOP_RESOURCE"},
+           {WARN_SAMESITE_NONE_INCLUDED_BY_SAMEPARTY_ANCESTORS,
+            "WARN_SAMESITE_NONE_INCLUDED_BY_SAMEPARTY_ANCESTORS"},
+           {WARN_SAMESITE_NONE_INCLUDED_BY_SAMESITE_LAX,
+            "WARN_SAMESITE_NONE_INCLUDED_BY_SAMESITE_LAX"},
+           {WARN_SAMESITE_NONE_INCLUDED_BY_SAMESITE_STRICT,
+            "WARN_SAMESITE_NONE_INCLUDED_BY_SAMESITE_STRICT"},
        }) {
     if (HasWarningReason(reason.first))
       base::StrAppend(&out, {reason.second, ", "});
diff --git a/net/cookies/cookie_inclusion_status.h b/net/cookies/cookie_inclusion_status.h
index 3247c62..ec394b9 100644
--- a/net/cookies/cookie_inclusion_status.h
+++ b/net/cookies/cookie_inclusion_status.h
@@ -176,6 +176,26 @@
     // TODO(crbug.com/1166211): Remove when no longer needed.
     WARN_SAMESITE_LAX_EXCLUDED_AFTER_BUGFIX_1166211 = 12,
 
+    // This cookie was SameSite=None and was included, but would have been
+    // excluded if it had been SameParty and the SameParty context had been
+    // computed using *either* top & current or the whole ancestor tree.
+    WARN_SAMESITE_NONE_REQUIRED = 13,
+    // This cookie was SameSite=None, was included, would have been included if
+    // it had been SameParty and the SameParty context type had been computed
+    // with only the top frame & resource URL, but would have been excluded if
+    // the SameParty context type had been computed using all ancestor frames.
+    WARN_SAMESITE_NONE_INCLUDED_BY_SAMEPARTY_TOP_RESOURCE = 14,
+    // This cookie was SameSite=None, was included, and would have been included
+    // if it had been SameParty and the SameParty context type had been computed
+    // using all ancestor frames.
+    WARN_SAMESITE_NONE_INCLUDED_BY_SAMEPARTY_ANCESTORS = 15,
+    // This cookie was SameSite=None, was included, and would have been included
+    // if it had been SameSite=Lax.
+    WARN_SAMESITE_NONE_INCLUDED_BY_SAMESITE_LAX = 16,
+    // This cookie was SameSite=None, was included, and would have been included
+    // if it had been SameSite=Strict.
+    WARN_SAMESITE_NONE_INCLUDED_BY_SAMESITE_STRICT = 17,
+
     // This should be kept last.
     NUM_WARNING_REASONS
   };
@@ -214,6 +234,8 @@
   explicit CookieInclusionStatus(ExclusionReason reason);
   // Makes a status that contains the given exclusion reason and warning.
   CookieInclusionStatus(ExclusionReason reason, WarningReason warning);
+  // Makes a status that contains the given warning.
+  explicit CookieInclusionStatus(WarningReason warning);
 
   bool operator==(const CookieInclusionStatus& other) const;
   bool operator!=(const CookieInclusionStatus& other) const;
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc
index 4c7ed978..60ede39 100644
--- a/net/cookies/cookie_monster_unittest.cc
+++ b/net/cookies/cookie_monster_unittest.cc
@@ -4103,7 +4103,9 @@
       {true, true, "A=B",  // not-recently-set session cookie.
        CookieInclusionStatus(), CookieEffectiveSameSite::LAX_MODE, kLongAge},
       // Cookie set from a secure URL with SameSite=None and Secure is set.
-      {true, true, "A=B; SameSite=None; Secure", CookieInclusionStatus(),
+      {true, true, "A=B; SameSite=None; Secure",
+       CookieInclusionStatus(
+           CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED),
        CookieEffectiveSameSite::NO_RESTRICTION},
       // Cookie set from a secure URL with SameSite=None but not specifying
       // Secure is rejected.
@@ -4137,15 +4139,20 @@
       {false, true, "A=B",  // not-recently-set session cookie.
        CookieInclusionStatus(), CookieEffectiveSameSite::LAX_MODE, kLongAge},
       // Cookie set from a secure URL with SameSite=None and Secure is set.
-      {false, true, "A=B; SameSite=None; Secure", CookieInclusionStatus(),
+      {false, true, "A=B; SameSite=None; Secure",
+       CookieInclusionStatus(
+           CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED),
        CookieEffectiveSameSite::NO_RESTRICTION},
       // Cookie set from an insecure URL with SameSite=None (which can't ever be
       // secure because it's an insecure URL) is NOT rejected, because
       // CookiesWithoutSameSiteMustBeSecure is not enabled.
       {false, false, "A=B; SameSite=None",
        CookieInclusionStatus::MakeFromReasonsForTesting(
-           std::vector<CookieInclusionStatus::ExclusionReason>(),
-           {CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE}),
+           {},
+           {
+               CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE,
+               CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED,
+           }),
        CookieEffectiveSameSite::NO_RESTRICTION},
       // Cookie set from an insecure URL which is defaulted into Lax is not
       // rejected.
diff --git a/net/cookies/cookie_options.cc b/net/cookies/cookie_options.cc
index 693cc91..7401ef9 100644
--- a/net/cookies/cookie_options.cc
+++ b/net/cookies/cookie_options.cc
@@ -8,6 +8,7 @@
 
 #include "base/metrics/histogram_functions.h"
 #include "net/cookies/cookie_util.h"
+#include "net/cookies/same_party_context.h"
 
 namespace net {
 
@@ -110,8 +111,7 @@
   options.set_include_httponly();
   options.set_same_site_cookie_context(SameSiteCookieContext::MakeInclusive());
   options.set_do_not_update_access_time();
-  options.set_same_party_cookie_context_type(
-      SamePartyCookieContextType::kSameParty);
+  options.set_same_party_context(SamePartyContext::MakeInclusive());
   options.set_is_in_nontrivial_first_party_set(true);
   return options;
 }
diff --git a/net/cookies/cookie_options.h b/net/cookies/cookie_options.h
index b4f9734..d275819f 100644
--- a/net/cookies/cookie_options.h
+++ b/net/cookies/cookie_options.h
@@ -13,6 +13,7 @@
 #include "net/base/net_export.h"
 #include "net/cookies/cookie_constants.h"
 #include "net/cookies/cookie_inclusion_status.h"
+#include "net/cookies/same_party_context.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
@@ -157,16 +158,6 @@
     ContextMetadata schemeful_metadata_;
   };
 
-  // Computed in URLRequestHttpJob for every cookie access attempt but is only
-  // relevant for SameParty cookies.
-  enum class SamePartyCookieContextType {
-    // The opposite to kSameParty. Should be the default value.
-    kCrossParty = 0,
-    // If the request URL is in the same First-Party Sets as the top-frame site
-    // and each member of the isolation_info.party_context.
-    kSameParty = 1,
-  };
-
   // Creates a CookieOptions object which:
   //
   // * Excludes HttpOnly cookies
@@ -211,14 +202,11 @@
   void unset_return_excluded_cookies() { return_excluded_cookies_ = false; }
   bool return_excluded_cookies() const { return return_excluded_cookies_; }
 
-  // How trusted is the current browser environment when it comes to accessing
-  // SameParty cookies. Default is not trusted, e.g. kCrossParty.
-  void set_same_party_cookie_context_type(
-      SamePartyCookieContextType context_type) {
-    same_party_cookie_context_type_ = context_type;
+  void set_same_party_context(const SamePartyContext& context) {
+    same_party_context_ = context;
   }
-  SamePartyCookieContextType same_party_cookie_context_type() const {
-    return same_party_cookie_context_type_;
+  const SamePartyContext& same_party_context() const {
+    return same_party_context_;
   }
 
   // Getter/setter of |full_party_context_size_| for logging purposes.
@@ -250,8 +238,8 @@
   bool update_access_time_ = true;
   bool return_excluded_cookies_ = false;
 
-  SamePartyCookieContextType same_party_cookie_context_type_ =
-      SamePartyCookieContextType::kCrossParty;
+  SamePartyContext same_party_context_;
+
   // The size of the isolation_info.party_context plus the top-frame site.
   // Stored for logging purposes.
   uint32_t full_party_context_size_ = 0;
diff --git a/net/cookies/cookie_util.cc b/net/cookies/cookie_util.cc
index f101d20..358306a 100644
--- a/net/cookies/cookie_util.cc
+++ b/net/cookies/cookie_util.cc
@@ -16,6 +16,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/notreached.h"
+#include "base/stl_util.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_tokenizer.h"
@@ -29,6 +30,7 @@
 #include "net/cookies/cookie_constants.h"
 #include "net/cookies/cookie_monster.h"
 #include "net/cookies/cookie_options.h"
+#include "net/cookies/same_party_context.h"
 #include "net/http/http_util.h"
 #include "url/gurl.h"
 #include "url/url_constants.h"
@@ -724,23 +726,23 @@
 // 1) `isolation_info` is not fully populated.
 // 2) `isolation_info.party_context` is null.
 // 3) `cookie_access_delegate.IsContextSamePartyWithSite` returns false.
-CookieOptions::SamePartyCookieContextType ComputeSamePartyContext(
+SamePartyContext ComputeSamePartyContext(
     const SchemefulSite& request_site,
     const IsolationInfo& isolation_info,
     const CookieAccessDelegate* cookie_access_delegate,
     bool force_ignore_top_frame_party) {
   if (!isolation_info.IsEmpty() && isolation_info.party_context().has_value() &&
-      cookie_access_delegate &&
-      cookie_access_delegate->IsContextSamePartyWithSite(
-          request_site,
-          force_ignore_top_frame_party
-              ? absl::nullopt
-              : isolation_info.network_isolation_key().GetTopFrameSite(),
-          isolation_info.party_context().value())) {
-    return CookieOptions::SamePartyCookieContextType::kSameParty;
+      cookie_access_delegate) {
+    return cookie_access_delegate->ComputeSamePartyContext(
+        request_site,
+        force_ignore_top_frame_party
+            ? nullptr
+            : base::OptionalOrNullptr(
+                  isolation_info.network_isolation_key().GetTopFrameSite()),
+        isolation_info.party_context().value());
   }
 
-  return CookieOptions::SamePartyCookieContextType::kCrossParty;
+  return SamePartyContext();
 }
 
 CookieSamePartyStatus GetSamePartyStatus(const CanonicalCookie& cookie,
@@ -750,10 +752,10 @@
     return CookieSamePartyStatus::kNoSamePartyEnforcement;
   }
 
-  switch (options.same_party_cookie_context_type()) {
-    case CookieOptions::SamePartyCookieContextType::kCrossParty:
+  switch (options.same_party_context().context_type()) {
+    case SamePartyContext::Type::kCrossParty:
       return CookieSamePartyStatus::kEnforceSamePartyExclude;
-    case CookieOptions::SamePartyCookieContextType::kSameParty:
+    case SamePartyContext::Type::kSameParty:
       return CookieSamePartyStatus::kEnforceSamePartyInclude;
   };
 }
diff --git a/net/cookies/cookie_util.h b/net/cookies/cookie_util.h
index aca464d..0f1910c 100644
--- a/net/cookies/cookie_util.h
+++ b/net/cookies/cookie_util.h
@@ -243,19 +243,16 @@
 
 NET_EXPORT bool IsFirstPartySetsEnabled();
 
-// Compute SameParty context, determines which of the cookies for `request_site`
-// can be accessed. Returns either kCrossParty or kSameParty. `isolation_info`
-// must be fully populated. In Chrome, all requests with credentials enabled
-// have a fully populated IsolationInfo.  But that might not be true for other
-// embedders yet (including cast, WebView, etc). Also not sure about iOS. If
+// Computes the SameParty context bundle, determining which of the cookies for
+// `request_site` can be accessed. `isolation_info` must be fully populated.  If
 // `force_ignore_top_frame_party` is true, the top frame from `isolation_info`
 // will be assumed to be same-party with `request_site`, regardless of what it
 // is.
-NET_EXPORT CookieOptions::SamePartyCookieContextType ComputeSamePartyContext(
-    const SchemefulSite& request_site,
-    const IsolationInfo& isolation_info,
-    const CookieAccessDelegate* cookie_access_delegate,
-    bool force_ignore_top_frame_party);
+NET_EXPORT SamePartyContext
+ComputeSamePartyContext(const SchemefulSite& request_site,
+                        const IsolationInfo& isolation_info,
+                        const CookieAccessDelegate* cookie_access_delegate,
+                        bool force_ignore_top_frame_party);
 
 NET_EXPORT FirstPartySetsContextType ComputeFirstPartySetsContextType(
     const SchemefulSite& request_site,
diff --git a/net/cookies/cookie_util_unittest.cc b/net/cookies/cookie_util_unittest.cc
index e41c755..06b5a0d 100644
--- a/net/cookies/cookie_util_unittest.cc
+++ b/net/cookies/cookie_util_unittest.cc
@@ -16,6 +16,7 @@
 #include "net/base/features.h"
 #include "net/cookies/cookie_constants.h"
 #include "net/cookies/cookie_util.h"
+#include "net/cookies/same_party_context.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -1483,9 +1484,9 @@
                  CookieSameSite::STRICT_MODE,
                  CookieSameSite::UNSPECIFIED,
              }) {
-          for (CookieOptions::SamePartyCookieContextType party_context_type : {
-                   CookieOptions::SamePartyCookieContextType::kCrossParty,
-                   CookieOptions::SamePartyCookieContextType::kSameParty,
+          for (SamePartyContext::Type party_context_type : {
+                   SamePartyContext::Type::kCrossParty,
+                   SamePartyContext::Type::kSameParty,
                }) {
             base::Time now = base::Time::Now();
             std::unique_ptr<CanonicalCookie> cookie =
@@ -1494,7 +1495,8 @@
                     secure, httponly, same_site,
                     CookiePriority::COOKIE_PRIORITY_DEFAULT, same_party);
 
-            options.set_same_party_cookie_context_type(party_context_type);
+            options.set_same_party_context(
+                SamePartyContext(party_context_type));
             EXPECT_EQ(CookieSamePartyStatus::kNoSamePartyEnforcement,
                       cookie_util::GetSamePartyStatus(*cookie, options));
           }
@@ -1519,9 +1521,9 @@
                  CookieSameSite::STRICT_MODE,
                  CookieSameSite::UNSPECIFIED,
              }) {
-          for (CookieOptions::SamePartyCookieContextType party_context_type : {
-                   CookieOptions::SamePartyCookieContextType::kCrossParty,
-                   CookieOptions::SamePartyCookieContextType::kSameParty,
+          for (SamePartyContext::Type party_context_type : {
+                   SamePartyContext::Type::kCrossParty,
+                   SamePartyContext::Type::kSameParty,
                }) {
             base::Time now = base::Time::Now();
             std::unique_ptr<CanonicalCookie> cookie =
@@ -1530,7 +1532,8 @@
                     secure, httponly, same_site,
                     CookiePriority::COOKIE_PRIORITY_DEFAULT, same_party);
 
-            options.set_same_party_cookie_context_type(party_context_type);
+            options.set_same_party_context(
+                SamePartyContext(party_context_type));
             EXPECT_EQ(CookieSamePartyStatus::kNoSamePartyEnforcement,
                       cookie_util::GetSamePartyStatus(*cookie, options));
           }
@@ -1554,9 +1557,9 @@
                CookieSameSite::STRICT_MODE,
                CookieSameSite::UNSPECIFIED,
            }) {
-        for (CookieOptions::SamePartyCookieContextType party_context_type : {
-                 CookieOptions::SamePartyCookieContextType::kCrossParty,
-                 CookieOptions::SamePartyCookieContextType::kSameParty,
+        for (SamePartyContext::Type party_context_type : {
+                 SamePartyContext::Type::kCrossParty,
+                 SamePartyContext::Type::kSameParty,
              }) {
           base::Time now = base::Time::Now();
           std::unique_ptr<CanonicalCookie> cookie =
@@ -1565,7 +1568,7 @@
                   httponly, same_site, CookiePriority::COOKIE_PRIORITY_DEFAULT,
                   false /* same_party */);
 
-          options.set_same_party_cookie_context_type(party_context_type);
+          options.set_same_party_context(SamePartyContext(party_context_type));
           EXPECT_EQ(CookieSamePartyStatus::kNoSamePartyEnforcement,
                     cookie_util::GetSamePartyStatus(*cookie, options));
         }
@@ -1622,13 +1625,13 @@
                   CookiePriority::COOKIE_PRIORITY_DEFAULT,
                   true /* same_party */);
 
-          options.set_same_party_cookie_context_type(
-              CookieOptions::SamePartyCookieContextType::kCrossParty);
+          options.set_same_party_context(
+              SamePartyContext(SamePartyContext::Type::kCrossParty));
           EXPECT_EQ(CookieSamePartyStatus::kEnforceSamePartyExclude,
                     cookie_util::GetSamePartyStatus(*cookie, options));
 
-          options.set_same_party_cookie_context_type(
-              CookieOptions::SamePartyCookieContextType::kSameParty);
+          options.set_same_party_context(
+              SamePartyContext(SamePartyContext::Type::kSameParty));
           EXPECT_EQ(CookieSamePartyStatus::kEnforceSamePartyInclude,
                     cookie_util::GetSamePartyStatus(*cookie, options));
         }
diff --git a/net/cookies/same_party_context.cc b/net/cookies/same_party_context.cc
new file mode 100644
index 0000000..70d0dd0
--- /dev/null
+++ b/net/cookies/same_party_context.cc
@@ -0,0 +1,33 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cookies/same_party_context.h"
+
+#include "net/cookies/cookie_constants.h"
+
+namespace net {
+
+SamePartyContext::SamePartyContext(Type type)
+    : SamePartyContext(type, type, type) {}
+
+SamePartyContext::SamePartyContext(Type context_type,
+                                   Type ancestors_for_metrics,
+                                   Type top_resource_for_metrics)
+    : context_type_(context_type),
+      ancestors_for_metrics_only_(ancestors_for_metrics),
+      top_resource_for_metrics_only_(top_resource_for_metrics) {}
+
+bool SamePartyContext::operator==(const SamePartyContext& other) const {
+  return std::make_tuple(context_type(), ancestors_for_metrics_only(),
+                         top_resource_for_metrics_only()) ==
+         std::make_tuple(other.context_type(),
+                         other.ancestors_for_metrics_only(),
+                         other.top_resource_for_metrics_only());
+}
+
+// static
+SamePartyContext SamePartyContext::MakeInclusive() {
+  return SamePartyContext(Type::kSameParty);
+}
+}  // namespace net
diff --git a/net/cookies/same_party_context.h b/net/cookies/same_party_context.h
new file mode 100644
index 0000000..819f641
--- /dev/null
+++ b/net/cookies/same_party_context.h
@@ -0,0 +1,63 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_COOKIES_SAME_PARTY_CONTEXT_H_
+#define NET_COOKIES_SAME_PARTY_CONTEXT_H_
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+// This struct bundles together a few different notions of same-party-ness.
+// `context_type()` gives the notion of same-party-ness that Chromium should use
+// in all cases except metrics; other accessors are just for metrics purposes,
+// to explore the impact of different definitions of "same-party".
+class NET_EXPORT SamePartyContext {
+ public:
+  // Computed in URLRequestHttpJob for every cookie access attempt but is only
+  // relevant for SameParty cookies.
+  enum class Type {
+    // The opposite to kSameParty. Should be the default value.
+    kCrossParty = 0,
+    // If the request URL is in the same First-Party Sets as the top-frame site
+    // and each member of the isolation_info.party_context.
+    kSameParty = 1,
+  };
+
+  SamePartyContext() = default;
+  explicit SamePartyContext(Type type);
+  SamePartyContext(Type context_type,
+                   Type ancestors_for_metrics,
+                   Type top_resource_for_metrics);
+
+  bool operator==(const SamePartyContext& other) const;
+
+  // How trusted is the current browser environment when it comes to accessing
+  // SameParty cookies. Default is not trusted, e.g. kCrossParty.
+  Type context_type() const { return context_type_; }
+
+  // We store the type of the SameParty context if we inferred singleton sets,
+  // for the purpose of metrics.
+  Type ancestors_for_metrics_only() const {
+    return ancestors_for_metrics_only_;
+  }
+  // We also store the type of the SameParty context if it were computed using
+  // only the top frame and resource URL and inferred singleton sets, for the
+  // purpose of metrics.
+  Type top_resource_for_metrics_only() const {
+    return top_resource_for_metrics_only_;
+  }
+
+  // Creates a SamePartyContext that is as permissive as possible.
+  static SamePartyContext MakeInclusive();
+
+ private:
+  Type context_type_ = Type::kCrossParty;
+  Type ancestors_for_metrics_only_ = Type::kCrossParty;
+  Type top_resource_for_metrics_only_ = Type::kCrossParty;
+};
+
+}  // namespace net
+
+#endif  // NET_COOKIES_SAME_PARTY_CONTEXT_H_
diff --git a/net/cookies/test_cookie_access_delegate.cc b/net/cookies/test_cookie_access_delegate.cc
index eb8c5d6..cacdb8cc 100644
--- a/net/cookies/test_cookie_access_delegate.cc
+++ b/net/cookies/test_cookie_access_delegate.cc
@@ -7,6 +7,7 @@
 #include "net/base/schemeful_site.h"
 #include "net/cookies/cookie_constants.h"
 #include "net/cookies/cookie_util.h"
+#include "net/cookies/same_party_context.h"
 
 namespace net {
 
@@ -34,11 +35,11 @@
   return true;
 }
 
-bool TestCookieAccessDelegate::IsContextSamePartyWithSite(
+SamePartyContext TestCookieAccessDelegate::ComputeSamePartyContext(
     const net::SchemefulSite& site,
-    const absl::optional<net::SchemefulSite>& top_frame_site,
+    const net::SchemefulSite* top_frame_site,
     const std::set<net::SchemefulSite>& party_context) const {
-  return false;
+  return SamePartyContext();
 }
 
 FirstPartySetsContextType
diff --git a/net/cookies/test_cookie_access_delegate.h b/net/cookies/test_cookie_access_delegate.h
index 674b3894..8a0320e 100644
--- a/net/cookies/test_cookie_access_delegate.h
+++ b/net/cookies/test_cookie_access_delegate.h
@@ -9,6 +9,7 @@
 
 #include "net/cookies/cookie_access_delegate.h"
 #include "net/cookies/cookie_constants.h"
+#include "net/cookies/same_party_context.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace net {
@@ -29,9 +30,9 @@
   bool ShouldIgnoreSameSiteRestrictions(
       const GURL& url,
       const SiteForCookies& site_for_cookies) const override;
-  bool IsContextSamePartyWithSite(
+  SamePartyContext ComputeSamePartyContext(
       const net::SchemefulSite& site,
-      const absl::optional<net::SchemefulSite>& top_frame_site,
+      const net::SchemefulSite* top_frame_site,
       const std::set<net::SchemefulSite>& party_context) const override;
   FirstPartySetsContextType ComputeFirstPartySetsContextType(
       const net::SchemefulSite& site,
diff --git a/net/http/http_proxy_connect_job.cc b/net/http/http_proxy_connect_job.cc
index 2cb7225..1f04eb19 100644
--- a/net/http/http_proxy_connect_job.cc
+++ b/net/http/http_proxy_connect_job.cc
@@ -41,6 +41,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 #include "url/gurl.h"
+#include "url/scheme_host_port.h"
 
 namespace net {
 
@@ -688,8 +689,12 @@
   quic::ParsedQuicVersion quic_version =
       common_connect_job_params()->quic_supported_versions->front();
   return quic_stream_request_->Request(
-      proxy_server, quic_version, ssl_params->privacy_mode(),
-      kH2QuicTunnelPriority, socket_tag(), params_->network_isolation_key(),
+      // TODO(crbug.com/1206799) Pass the destination directly once it's
+      // converted to contain scheme.
+      url::SchemeHostPort(url::kHttpsScheme, proxy_server.host(),
+                          proxy_server.port()),
+      quic_version, ssl_params->privacy_mode(), kH2QuicTunnelPriority,
+      socket_tag(), params_->network_isolation_key(),
       ssl_params->GetDirectConnectionParams()->secure_dns_policy(),
       /*use_dns_aliases=*/false, ssl_params->ssl_config().GetCertVerifyFlags(),
       GURL("https://" + proxy_server.ToString()), net_log(),
diff --git a/net/http/http_stream_factory_job.cc b/net/http/http_stream_factory_job.cc
index 54d4c77..082d072 100644
--- a/net/http/http_stream_factory_job.cc
+++ b/net/http/http_stream_factory_job.cc
@@ -852,17 +852,18 @@
 }
 
 int HttpStreamFactory::Job::DoInitConnectionImplQuic() {
-  HostPortPair destination;
+  url::SchemeHostPort destination;
   SSLConfig* ssl_config;
   GURL url(request_info_.url);
   if (proxy_info_.is_quic()) {
-    // A proxy's certificate is expected to be valid for the proxy hostname.
-    destination = proxy_info_.proxy_server().host_port_pair();
     ssl_config = &proxy_ssl_config_;
     GURL::Replacements replacements;
     replacements.SetSchemeStr(url::kHttpsScheme);
-    replacements.SetHostStr(destination.host());
-    const std::string new_port = base::NumberToString(destination.port());
+    const HostPortPair& proxy_endpoint =
+        proxy_info_.proxy_server().host_port_pair();
+    // A proxy's certificate is expected to be valid for the proxy hostname.
+    replacements.SetHostStr(proxy_endpoint.host());
+    const std::string new_port = base::NumberToString(proxy_endpoint.port());
     replacements.SetPortStr(new_port);
     replacements.ClearUsername();
     replacements.ClearPassword();
@@ -870,17 +871,17 @@
     replacements.ClearQuery();
     replacements.ClearRef();
     url = url.ReplaceComponents(replacements);
+    destination = url::SchemeHostPort(url);
   } else {
     DCHECK(using_ssl_);
     // The certificate of a QUIC alternative server is expected to be valid
     // for the origin of the request (in addition to being valid for the
     // server itself).
-    destination = HostPortPair::FromSchemeHostPort(destination_);
+    destination = destination_;
     ssl_config = &server_ssl_config_;
   }
   DCHECK(url.SchemeIs(url::kHttpsScheme));
 
-  // TODO(crbug.com/1206799): Pass scheme to QUIC request.
   int rv = quic_request_.Request(
       std::move(destination), quic_version_, request_info_.privacy_mode,
       priority_, request_info_.socket_tag, request_info_.network_isolation_key,
diff --git a/net/http/http_stream_factory_job_controller.cc b/net/http/http_stream_factory_job_controller.cc
index 9a96bf8..7693e32 100644
--- a/net/http/http_stream_factory_job_controller.cc
+++ b/net/http/http_stream_factory_job_controller.cc
@@ -1098,9 +1098,8 @@
     }
     RewriteUrlWithHostMappingRules(destination);
 
-    // TODO(crbug.com/1206799): Pass scheme to CanUseExistingSession().
     if (session_->quic_stream_factory()->CanUseExistingSession(
-            session_key, HostPortPair::FromURL(destination)))
+            session_key, url::SchemeHostPort(destination)))
       return alternative_service_info;
 
     if (!IsQuicAllowedForHost(destination.host()))
diff --git a/net/quic/bidirectional_stream_quic_impl_unittest.cc b/net/quic/bidirectional_stream_quic_impl_unittest.cc
index 82b387a0..83cedfda 100644
--- a/net/quic/bidirectional_stream_quic_impl_unittest.cc
+++ b/net/quic/bidirectional_stream_quic_impl_unittest.cc
@@ -62,6 +62,8 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/scheme_host_port.h"
+#include "url/url_constants.h"
 
 namespace net {
 
@@ -463,7 +465,9 @@
                       false),
         random_generator_(0),
         printer_(version_),
-        destination_(kDefaultServerHostName, kDefaultServerPort) {
+        destination_(url::kHttpsScheme,
+                     kDefaultServerHostName,
+                     kDefaultServerPort) {
     quic::QuicEnableVersion(version_);
     FLAGS_quic_enable_http3_grease_randomness = false;
     IPAddress ip(192, 0, 2, 33);
@@ -845,7 +849,7 @@
   MockCryptoClientStreamFactory crypto_client_stream_factory_;
   std::unique_ptr<StaticSocketDataProvider> socket_data_;
   std::vector<PacketToWrite> writes_;
-  HostPortPair destination_;
+  url::SchemeHostPort destination_;
   quic::test::NoopQpackStreamSenderDelegate noop_qpack_stream_sender_delegate_;
 };
 
diff --git a/net/quic/dedicated_web_transport_http3_client.cc b/net/quic/dedicated_web_transport_http3_client.cc
index 78005b0..a972375 100644
--- a/net/quic/dedicated_web_transport_http3_client.cc
+++ b/net/quic/dedicated_web_transport_http3_client.cc
@@ -19,6 +19,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_connection.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
 #include "net/url_request/url_request_context.h"
+#include "url/scheme_host_port.h"
 
 namespace net {
 
@@ -339,7 +340,7 @@
   next_connect_state_ = CONNECT_STATE_RESOLVE_HOST_COMPLETE;
   HostResolver::ResolveHostParameters parameters;
   resolve_host_request_ = context_->host_resolver()->CreateRequest(
-      HostPortPair::FromURL(url_), isolation_key_, net_log_, absl::nullopt);
+      url::SchemeHostPort(url_), isolation_key_, net_log_, absl::nullopt);
   return resolve_host_request_->Start(base::BindOnce(
       &DedicatedWebTransportHttp3Client::DoLoop, base::Unretained(this)));
 }
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index 094d1cd..15f25429 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -56,6 +56,7 @@
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "third_party/boringssl/src/include/openssl/ssl.h"
 #include "url/origin.h"
+#include "url/scheme_host_port.h"
 
 namespace net {
 
@@ -358,10 +359,10 @@
 
 QuicChromiumClientSession::Handle::Handle(
     const base::WeakPtr<QuicChromiumClientSession>& session,
-    const HostPortPair& destination)
+    url::SchemeHostPort destination)
     : MultiplexedSessionHandle(session),
       session_(session),
-      destination_(destination),
+      destination_(std::move(destination)),
       net_log_(session_->net_log()),
       was_handshake_confirmed_(session->OneRttKeysAvailable()),
       net_error_(OK),
@@ -3420,9 +3421,9 @@
 }
 
 std::unique_ptr<QuicChromiumClientSession::Handle>
-QuicChromiumClientSession::CreateHandle(const HostPortPair& destination) {
+QuicChromiumClientSession::CreateHandle(url::SchemeHostPort destination) {
   return std::make_unique<QuicChromiumClientSession::Handle>(
-      weak_factory_.GetWeakPtr(), destination);
+      weak_factory_.GetWeakPtr(), std::move(destination));
 }
 
 bool QuicChromiumClientSession::OnReadError(
diff --git a/net/quic/quic_chromium_client_session.h b/net/quic/quic_chromium_client_session.h
index 853970a..6ee1230 100644
--- a/net/quic/quic_chromium_client_session.h
+++ b/net/quic/quic_chromium_client_session.h
@@ -53,6 +53,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_time.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "url/origin.h"
+#include "url/scheme_host_port.h"
 
 namespace net {
 
@@ -196,7 +197,7 @@
     // Constructs a handle to |session| which was created via the alternative
     // server |destination|.
     Handle(const base::WeakPtr<QuicChromiumClientSession>& session,
-           const HostPortPair& destination);
+           url::SchemeHostPort destination);
     Handle(const Handle& other) = delete;
     ~Handle() override;
 
@@ -267,7 +268,7 @@
     quic::QuicServerId server_id() const { return server_id_; }
 
     // Returns the alternative server used for this session.
-    HostPortPair destination() const { return destination_; }
+    const url::SchemeHostPort& destination() const { return destination_; }
 
     // Returns the session's net log.
     const NetLogWithSource& net_log() const { return net_log_; }
@@ -323,7 +324,7 @@
     // Underlying session which may be destroyed before this handle.
     base::WeakPtr<QuicChromiumClientSession> session_;
 
-    HostPortPair destination_;
+    url::SchemeHostPort destination_;
 
     // Stream request created by |RequestStream()|.
     std::unique_ptr<StreamRequest> stream_request_;
@@ -744,7 +745,7 @@
 
   // Returns a Handle to this session.
   std::unique_ptr<QuicChromiumClientSession::Handle> CreateHandle(
-      const HostPortPair& destination);
+      url::SchemeHostPort destination);
 
   // Returns the number of client hello messages that have been sent on the
   // crypto stream. If the handshake has completed then this is one greater
diff --git a/net/quic/quic_chromium_client_session_test.cc b/net/quic/quic_chromium_client_session_test.cc
index 9c1c942c..bff163e 100644
--- a/net/quic/quic_chromium_client_session_test.cc
+++ b/net/quic/quic_chromium_client_session_test.cc
@@ -70,6 +70,8 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "url/gurl.h"
+#include "url/scheme_host_port.h"
+#include "url/url_constants.h"
 
 using testing::_;
 
@@ -144,7 +146,7 @@
                      SocketTag(),
                      NetworkIsolationKey(),
                      SecureDnsPolicy::kAllow),
-        destination_(kServerHostname, kServerPort),
+        destination_(url::kHttpsScheme, kServerHostname, kServerPort),
         default_network_(NetworkChangeNotifier::kInvalidNetworkHandle),
         client_maker_(version_,
                       quic::QuicUtils::CreateRandomConnectionId(&random_),
@@ -308,7 +310,7 @@
   std::unique_ptr<TransportSecurityState> transport_security_state_;
   MockCryptoClientStreamFactory crypto_client_stream_factory_;
   QuicSessionKey session_key_;
-  HostPortPair destination_;
+  url::SchemeHostPort destination_;
   std::unique_ptr<TestingQuicChromiumClientSession> session_;
   NetworkChangeNotifier::NetworkHandle default_network_;
   std::unique_ptr<QuicConnectivityMonitor> connectivity_monitor_;
diff --git a/net/quic/quic_http_stream.cc b/net/quic/quic_http_stream.cc
index e62f095..4c24a1be 100644
--- a/net/quic/quic_http_stream.cc
+++ b/net/quic/quic_http_stream.cc
@@ -30,6 +30,7 @@
 #include "net/third_party/quiche/src/spdy/core/spdy_frame_builder.h"
 #include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
 #include "url/origin.h"
+#include "url/scheme_host_port.h"
 
 namespace net {
 
@@ -408,7 +409,7 @@
 bool QuicHttpStream::GetAlternativeService(
     AlternativeService* alternative_service) const {
   alternative_service->protocol = kProtoQUIC;
-  const HostPortPair& destination = quic_session()->destination();
+  const url::SchemeHostPort& destination = quic_session()->destination();
   alternative_service->host = destination.host();
   alternative_service->port = destination.port();
   return true;
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
index 98229ce..d1310ff 100644
--- a/net/quic/quic_http_stream_test.cc
+++ b/net/quic/quic_http_stream_test.cc
@@ -78,6 +78,8 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/scheme_host_port.h"
+#include "url/url_constants.h"
 
 using std::string;
 using testing::_;
@@ -433,10 +435,12 @@
     TestCompletionCallback callback;
     session_->CryptoConnect(callback.callback());
     stream_ = std::make_unique<QuicHttpStream>(
-        session_->CreateHandle(HostPortPair("www.example.org", 443)),
+        session_->CreateHandle(
+            url::SchemeHostPort(url::kHttpsScheme, "www.example.org", 443)),
         std::vector<std::string>() /* dns_aliases */);
     promised_stream_ = std::make_unique<QuicHttpStream>(
-        session_->CreateHandle(HostPortPair("www.example.org", 443)),
+        session_->CreateHandle(
+            url::SchemeHostPort(url::kHttpsScheme, "www.example.org", 443)),
         std::vector<std::string>() /* dns_aliases */);
     push_promise_[":path"] = "/bar";
     push_promise_[":authority"] = "www.example.org";
@@ -827,9 +831,9 @@
             stream_->SendRequest(headers_, &response_, callback_.callback()));
 
   // Start a second request.
-  QuicHttpStream stream2(
-      session_->CreateHandle(HostPortPair("www.example.org", 443)),
-      {} /* dns_aliases */);
+  QuicHttpStream stream2(session_->CreateHandle(url::SchemeHostPort(
+                             url::kHttpsScheme, "www.example.org", 443)),
+                         {} /* dns_aliases */);
   TestCompletionCallback callback2;
   EXPECT_EQ(OK,
             stream2.InitializeStream(&request_, true, DEFAULT_PRIORITY,
@@ -1040,7 +1044,8 @@
       DEFAULT_PRIORITY, &spdy_request_header_frame_length));
 
   auto stream = std::make_unique<QuicHttpStream>(
-      session_->CreateHandle(HostPortPair("www.example.org/foo", 443)),
+      session_->CreateHandle(
+          url::SchemeHostPort(url::kHttpsScheme, "www.example.org/foo", 443)),
       std::vector<std::string>() /* dns_aliases */);
   EXPECT_THAT(stream->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                        net_log_.bound(), callback_.callback()),
diff --git a/net/quic/quic_proxy_client_socket_unittest.cc b/net/quic/quic_proxy_client_socket_unittest.cc
index d2a8038..3b22a2f 100644
--- a/net/quic/quic_proxy_client_socket_unittest.cc
+++ b/net/quic/quic_proxy_client_socket_unittest.cc
@@ -56,6 +56,8 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/scheme_host_port.h"
+#include "url/url_constants.h"
 
 using testing::_;
 using testing::AnyNumber;
@@ -179,8 +181,8 @@
                       false),
         random_generator_(0),
         user_agent_(kUserAgent),
-        proxy_host_port_(kProxyHost, kProxyPort),
-        endpoint_host_port_(kOriginHost, kOriginPort),
+        proxy_endpoint_(url::kHttpsScheme, kProxyHost, kProxyPort),
+        destination_endpoint_(url::kHttpsScheme, kOriginHost, kOriginPort),
         http_auth_cache_(
             false /* key_server_entries_by_network_isolation_key */),
         host_resolver_(new MockCachingHostResolver()),
@@ -283,8 +285,8 @@
 
     writer->set_delegate(session_.get());
 
-    session_handle_ =
-        session_->CreateHandle(HostPortPair("mail.example.org", 80));
+    session_handle_ = session_->CreateHandle(
+        url::SchemeHostPort(url::kHttpsScheme, "mail.example.org", 80));
 
     session_->Initialize();
 
@@ -307,10 +309,16 @@
 
     sock_ = std::make_unique<QuicProxyClientSocket>(
         std::move(stream_handle), std::move(session_handle_),
-        ProxyServer(ProxyServer::SCHEME_HTTPS, proxy_host_port_), user_agent_,
-        endpoint_host_port_, net_log_.bound(),
-        new HttpAuthController(HttpAuth::AUTH_PROXY,
-                               GURL("https://" + proxy_host_port_.ToString()),
+        // TODO(crbug.com/1206799) Construct `ProxyServer` with plain
+        // `proxy_endpoint_` once it supports `url::SchemeHostPort`.
+        ProxyServer(ProxyServer::SCHEME_HTTPS,
+                    HostPortPair::FromSchemeHostPort(proxy_endpoint_)),
+        user_agent_,
+        // TODO(crbug.com/1206799) Construct `QuicProxyClientSocket` with plain
+        // `proxy_endpoint_` once it supports `url::SchemeHostPort`.
+        HostPortPair::FromSchemeHostPort(destination_endpoint_),
+        net_log_.bound(),
+        new HttpAuthController(HttpAuth::AUTH_PROXY, proxy_endpoint_.GetURL(),
                                NetworkIsolationKey(), &http_auth_cache_,
                                http_auth_handler_factory_.get(),
                                host_resolver_.get()),
@@ -321,7 +329,8 @@
 
   void PopulateConnectRequestIR(spdy::Http2HeaderBlock* block) {
     (*block)[":method"] = "CONNECT";
-    (*block)[":authority"] = endpoint_host_port_.ToString();
+    (*block)[":authority"] =
+        HostPortPair::FromSchemeHostPort(destination_endpoint_).ToString();
     (*block)["user-agent"] = kUserAgent;
   }
 
@@ -380,7 +389,8 @@
       RequestPriority request_priority = LOWEST) {
     spdy::Http2HeaderBlock block;
     block[":method"] = "CONNECT";
-    block[":authority"] = endpoint_host_port_.ToString();
+    block[":authority"] =
+        HostPortPair::FromSchemeHostPort(destination_endpoint_).ToString();
     for (const auto& header : extra_headers) {
       block[header.first] = header.second;
     }
@@ -625,8 +635,8 @@
   MockCryptoClientStreamFactory crypto_client_stream_factory_;
 
   std::string user_agent_;
-  HostPortPair proxy_host_port_;
-  HostPortPair endpoint_host_port_;
+  url::SchemeHostPort proxy_endpoint_;
+  url::SchemeHostPort destination_endpoint_;
   HttpAuthCache http_auth_cache_;
   std::unique_ptr<MockHostResolverBase> host_resolver_;
   std::unique_ptr<HttpAuthHandlerRegistryFactory> http_auth_handler_factory_;
@@ -668,7 +678,10 @@
 
 TEST_P(QuicProxyClientSocketTest, ProxyDelegateExtraHeaders) {
   proxy_delegate_ = std::make_unique<TestProxyDelegate>();
-  ProxyServer proxy_server(ProxyServer::SCHEME_HTTPS, proxy_host_port_);
+  // TODO(crbug.com/1206799) Construct `ProxyServer` with plain
+  // `proxy_endpoint_` once it supports `url::SchemeHostPort`.
+  ProxyServer proxy_server(ProxyServer::SCHEME_HTTPS,
+                           HostPortPair::FromSchemeHostPort(proxy_endpoint_));
 
   const char kResponseHeaderName[] = "foo";
   const char kResponseHeaderValue[] = "testing";
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index d7cf042..2ce8734 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -72,6 +72,7 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
 #include "third_party/boringssl/src/include/openssl/aead.h"
 #include "url/gurl.h"
+#include "url/scheme_host_port.h"
 #include "url/url_constants.h"
 
 using NetworkHandle = net::NetworkChangeNotifier::NetworkHandle;
@@ -973,7 +974,7 @@
 }
 
 int QuicStreamRequest::Request(
-    const HostPortPair& destination,
+    url::SchemeHostPort destination,
     quic::ParsedQuicVersion quic_version,
     PrivacyMode privacy_mode,
     RequestPriority priority,
@@ -1000,9 +1001,9 @@
       QuicSessionKey(HostPortPair::FromURL(url), privacy_mode, socket_tag,
                      network_isolation_key, secure_dns_policy);
 
-  int rv =
-      factory_->Create(session_key_, destination, quic_version, priority,
-                       use_dns_aliases, cert_verify_flags, url, net_log, this);
+  int rv = factory_->Create(session_key_, std::move(destination), quic_version,
+                            priority, use_dns_aliases, cert_verify_flags, url,
+                            net_log, this);
   if (rv == ERR_IO_PENDING) {
     net_log_ = net_log;
     callback_ = std::move(callback);
@@ -1071,9 +1072,10 @@
 }
 
 QuicStreamFactory::QuicSessionAliasKey::QuicSessionAliasKey(
-    const HostPortPair& destination,
-    const QuicSessionKey& session_key)
-    : destination_(destination), session_key_(session_key) {}
+    url::SchemeHostPort destination,
+    QuicSessionKey session_key)
+    : destination_(std::move(destination)),
+      session_key_(std::move(session_key)) {}
 
 bool QuicStreamFactory::QuicSessionAliasKey::operator<(
     const QuicSessionAliasKey& other) const {
@@ -1083,7 +1085,7 @@
 
 bool QuicStreamFactory::QuicSessionAliasKey::operator==(
     const QuicSessionAliasKey& other) const {
-  return destination_.Equals(other.destination_) &&
+  return destination_ == other.destination_ &&
          session_key_ == other.session_key_;
 }
 
@@ -1172,14 +1174,15 @@
   }
 }
 
-bool QuicStreamFactory::CanUseExistingSession(const QuicSessionKey& session_key,
-                                              const HostPortPair& destination) {
+bool QuicStreamFactory::CanUseExistingSession(
+    const QuicSessionKey& session_key,
+    const url::SchemeHostPort& destination) {
   if (base::Contains(active_sessions_, session_key))
     return true;
 
   for (const auto& key_value : active_sessions_) {
     QuicChromiumClientSession* session = key_value.second;
-    if (destination.Equals(all_sessions_[session].destination()) &&
+    if (destination == all_sessions_[session].destination() &&
         session->CanPool(session_key.host(), session_key)) {
       return true;
     }
@@ -1189,7 +1192,7 @@
 }
 
 int QuicStreamFactory::Create(const QuicSessionKey& session_key,
-                              const HostPortPair& destination,
+                              url::SchemeHostPort destination,
                               quic::ParsedQuicVersion quic_version,
                               RequestPriority priority,
                               bool use_dns_aliases,
@@ -1212,7 +1215,7 @@
     if (!promised)
       continue;
     DCHECK_EQ(promised->session(), session.second);
-    request->SetSession(session.second->CreateHandle(destination));
+    request->SetSession(session.second->CreateHandle(std::move(destination)));
     ++num_push_streams_created_;
     return OK;
   }
@@ -1221,7 +1224,7 @@
   auto active_session = active_sessions_.find(session_key);
   if (active_session != active_sessions_.end()) {
     QuicChromiumClientSession* session = active_session->second;
-    request->SetSession(session->CreateHandle(destination));
+    request->SetSession(session->CreateHandle(std::move(destination)));
     return OK;
   }
 
@@ -1243,9 +1246,9 @@
   if (!active_sessions_.empty()) {
     for (const auto& key_value : active_sessions_) {
       QuicChromiumClientSession* session = key_value.second;
-      if (destination.Equals(all_sessions_[session].destination()) &&
+      if (destination == all_sessions_[session].destination() &&
           session->CanPool(session_key.server_id().host(), session_key)) {
-        request->SetSession(session->CreateHandle(destination));
+        request->SetSession(session->CreateHandle(std::move(destination)));
         return OK;
       }
     }
@@ -1280,7 +1283,7 @@
     if (it == active_sessions_.end())
       return ERR_QUIC_PROTOCOL_ERROR;
     QuicChromiumClientSession* session = it->second;
-    request->SetSession(session->CreateHandle(destination));
+    request->SetSession(session->CreateHandle(std::move(destination)));
   }
   return rv;
 }
diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h
index d825a1f1..975f126 100644
--- a/net/quic/quic_stream_factory.h
+++ b/net/quic/quic_stream_factory.h
@@ -45,6 +45,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
 #include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "url/scheme_host_port.h"
 
 namespace base {
 class Value;
@@ -122,7 +123,7 @@
   // When |use_dns_aliases| is true, any DNS aliases found in host resolution
   // are stored in the |dns_aliases_by_session_key_| map. |use_dns_aliases|
   // should be false in the case of a proxy.
-  int Request(const HostPortPair& destination,
+  int Request(url::SchemeHostPort destination,
               quic::ParsedQuicVersion quic_version,
               PrivacyMode privacy_mode,
               RequestPriority priority,
@@ -214,15 +215,15 @@
   class NET_EXPORT_PRIVATE QuicSessionAliasKey {
    public:
     QuicSessionAliasKey() = default;
-    QuicSessionAliasKey(const HostPortPair& destination,
-                        const QuicSessionKey& session_key);
+    QuicSessionAliasKey(url::SchemeHostPort destination,
+                        QuicSessionKey session_key);
     ~QuicSessionAliasKey() = default;
 
     // Needed to be an element of std::set.
     bool operator<(const QuicSessionAliasKey& other) const;
     bool operator==(const QuicSessionAliasKey& other) const;
 
-    const HostPortPair& destination() const { return destination_; }
+    const url::SchemeHostPort& destination() const { return destination_; }
     const quic::QuicServerId& server_id() const {
       return session_key_.server_id();
     }
@@ -231,7 +232,7 @@
     // Returns the estimate of dynamically allocated memory in bytes.
 
    private:
-    HostPortPair destination_;
+    url::SchemeHostPort destination_;
     QuicSessionKey session_key_;
   };
 
@@ -254,7 +255,7 @@
   // request can be pooled to an existing session to the IP address of
   // |destination|.
   bool CanUseExistingSession(const QuicSessionKey& session_key,
-                             const HostPortPair& destination);
+                             const url::SchemeHostPort& destination);
 
   // Fetches a QuicChromiumClientSession to |host_port_pair| which will be
   // owned by |request|.
@@ -265,7 +266,7 @@
   // are stored in the |dns_aliases_by_session_key_| map. |use_dns_aliases|
   // should be false in the case of a proxy.
   int Create(const QuicSessionKey& session_key,
-             const HostPortPair& destination,
+             url::SchemeHostPort destination,
              quic::ParsedQuicVersion quic_version,
              RequestPriority priority,
              bool use_dns_aliases,
diff --git a/net/quic/quic_stream_factory_fuzzer.cc b/net/quic/quic_stream_factory_fuzzer.cc
index 8649989..3fce1d5 100644
--- a/net/quic/quic_stream_factory_fuzzer.cc
+++ b/net/quic/quic_stream_factory_fuzzer.cc
@@ -28,6 +28,8 @@
 #include "net/ssl/ssl_config_service_defaults.h"
 #include "net/test/gtest_util.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "url/scheme_host_port.h"
+#include "url/url_constants.h"
 
 namespace net {
 
@@ -53,7 +55,7 @@
 
 // Static initialization for persistent factory data
 struct Env {
-  Env() : host_port_pair(kServerHostName, kServerPort) {
+  Env() : scheme_host_port(url::kHttpsScheme, kServerHostName, kServerPort) {
     quic_context.AdvanceTime(quic::QuicTime::Delta::FromSeconds(1));
     ssl_config_service = std::make_unique<SSLConfigServiceDefaults>();
     crypto_client_stream_factory.set_use_mock_crypter(true);
@@ -67,7 +69,7 @@
   std::unique_ptr<SSLConfigService> ssl_config_service;
   ProofVerifyDetailsChromium verify_details;
   MockCryptoClientStreamFactory crypto_client_stream_factory;
-  HostPortPair host_port_pair;
+  url::SchemeHostPort scheme_host_port;
   NetLogWithSource net_log;
   std::unique_ptr<CertVerifier> cert_verifier;
   TransportSecurityState transport_security_state;
@@ -147,7 +149,7 @@
   quic::QuicEnableVersion(version);
 
   request.Request(
-      env->host_port_pair, version, PRIVACY_MODE_DISABLED, DEFAULT_PRIORITY,
+      env->scheme_host_port, version, PRIVACY_MODE_DISABLED, DEFAULT_PRIORITY,
       SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
       true /* use_dns_aliases */, kCertVerifyFlags, GURL(kUrl), env->net_log,
       &net_error_details,
diff --git a/net/quic/quic_stream_factory_peer.cc b/net/quic/quic_stream_factory_peer.cc
index 0770124..c03d1320 100644
--- a/net/quic/quic_stream_factory_peer.cc
+++ b/net/quic/quic_stream_factory_peer.cc
@@ -17,6 +17,7 @@
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
 #include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h"
+#include "url/scheme_host_port.h"
 
 using std::string;
 
@@ -53,10 +54,11 @@
 QuicChromiumClientSession* QuicStreamFactoryPeer::GetPendingSession(
     QuicStreamFactory* factory,
     const quic::QuicServerId& server_id,
-    const HostPortPair& destination) {
+    url::SchemeHostPort destination) {
   QuicSessionKey session_key(server_id, SocketTag(), NetworkIsolationKey(),
                              SecureDnsPolicy::kAllow);
-  QuicStreamFactory::QuicSessionAliasKey key(destination, session_key);
+  QuicStreamFactory::QuicSessionAliasKey key(std::move(destination),
+                                             session_key);
   DCHECK(factory->HasActiveJob(session_key));
   DCHECK_EQ(factory->all_sessions_.size(), 1u);
   DCHECK(key == factory->all_sessions_.begin()->second);
@@ -75,12 +77,12 @@
 
 bool QuicStreamFactoryPeer::HasLiveSession(
     QuicStreamFactory* factory,
-    const HostPortPair& destination,
+    url::SchemeHostPort destination,
     const quic::QuicServerId& server_id) {
   QuicSessionKey session_key = QuicSessionKey(
       server_id, SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow);
-  QuicStreamFactory::QuicSessionAliasKey alias_key =
-      QuicStreamFactory::QuicSessionAliasKey(destination, session_key);
+  QuicStreamFactory::QuicSessionAliasKey alias_key(std::move(destination),
+                                                   session_key);
   for (auto it = factory->all_sessions_.begin();
        it != factory->all_sessions_.end(); ++it) {
     if (it->second == alias_key)
diff --git a/net/quic/quic_stream_factory_peer.h b/net/quic/quic_stream_factory_peer.h
index 0e25bad..adc782f 100644
--- a/net/quic/quic_stream_factory_peer.h
+++ b/net/quic/quic_stream_factory_peer.h
@@ -19,6 +19,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
 #include "net/third_party/quiche/src/quic/core/quic_server_id.h"
 #include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "url/scheme_host_port.h"
 
 namespace quic {
 class QuicAlarmFactory;
@@ -53,7 +54,7 @@
   static QuicChromiumClientSession* GetPendingSession(
       QuicStreamFactory* factory,
       const quic::QuicServerId& server_id,
-      const HostPortPair& destination);
+      url::SchemeHostPort destination);
 
   static QuicChromiumClientSession* GetActiveSession(
       QuicStreamFactory* factory,
@@ -61,7 +62,7 @@
       const NetworkIsolationKey& network_isolation_key = NetworkIsolationKey());
 
   static bool HasLiveSession(QuicStreamFactory* factory,
-                             const HostPortPair& destination,
+                             url::SchemeHostPort destination,
                              const quic::QuicServerId& server_id);
 
   static bool IsLiveSession(QuicStreamFactory* factory,
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index 4f6ff620..c77847c 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -82,6 +82,8 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
+#include "url/scheme_host_port.h"
+#include "url/url_constants.h"
 
 using std::string;
 
@@ -217,7 +219,9 @@
         cert_verifier_(std::make_unique<MockCertVerifier>()),
         scoped_mock_network_change_notifier_(nullptr),
         factory_(nullptr),
-        host_port_pair_(kDefaultServerHostName, kDefaultServerPort),
+        scheme_host_port_(url::kHttpsScheme,
+                          kDefaultServerHostName,
+                          kDefaultServerPort),
         url_(kDefaultUrl),
         url2_(kServer2Url),
         url3_(kServer3Url),
@@ -299,57 +303,59 @@
                                             std::move(dns_aliases));
   }
 
-  bool HasActiveSession(const HostPortPair& host_port_pair,
+  bool HasActiveSession(const url::SchemeHostPort& scheme_host_port,
                         const NetworkIsolationKey& network_isolation_key =
                             NetworkIsolationKey()) {
-    quic::QuicServerId server_id(host_port_pair.host(), host_port_pair.port(),
-                                 false);
+    quic::QuicServerId server_id(scheme_host_port.host(),
+                                 scheme_host_port.port(), false);
     return QuicStreamFactoryPeer::HasActiveSession(factory_.get(), server_id,
                                                    network_isolation_key);
   }
 
-  bool HasLiveSession(const HostPortPair& host_port_pair) {
-    quic::QuicServerId server_id(host_port_pair.host(), host_port_pair.port(),
-                                 false);
-    return QuicStreamFactoryPeer::HasLiveSession(factory_.get(), host_port_pair,
-                                                 server_id);
+  bool HasLiveSession(const url::SchemeHostPort& scheme_host_port) {
+    quic::QuicServerId server_id(scheme_host_port.host(),
+                                 scheme_host_port.port(), false);
+    return QuicStreamFactoryPeer::HasLiveSession(factory_.get(),
+                                                 scheme_host_port, server_id);
   }
 
-  bool HasActiveJob(const HostPortPair& host_port_pair,
+  bool HasActiveJob(const url::SchemeHostPort& scheme_host_port,
                     const PrivacyMode privacy_mode) {
-    quic::QuicServerId server_id(host_port_pair.host(), host_port_pair.port(),
+    quic::QuicServerId server_id(scheme_host_port.host(),
+                                 scheme_host_port.port(),
                                  privacy_mode == PRIVACY_MODE_ENABLED);
     return QuicStreamFactoryPeer::HasActiveJob(factory_.get(), server_id);
   }
 
   // Get the pending, not activated session, if there is only one session alive.
   QuicChromiumClientSession* GetPendingSession(
-      const HostPortPair& host_port_pair) {
-    quic::QuicServerId server_id(host_port_pair.host(), host_port_pair.port(),
-                                 false);
+      const url::SchemeHostPort& scheme_host_port) {
+    quic::QuicServerId server_id(scheme_host_port.host(),
+                                 scheme_host_port.port(), false);
     return QuicStreamFactoryPeer::GetPendingSession(factory_.get(), server_id,
-                                                    host_port_pair);
+                                                    scheme_host_port);
   }
 
   QuicChromiumClientSession* GetActiveSession(
-      const HostPortPair& host_port_pair,
+      const url::SchemeHostPort& scheme_host_port,
       const NetworkIsolationKey& network_isolation_key =
           NetworkIsolationKey()) {
-    quic::QuicServerId server_id(host_port_pair.host(), host_port_pair.port(),
-                                 false);
+    quic::QuicServerId server_id(scheme_host_port.host(),
+                                 scheme_host_port.port(), false);
     return QuicStreamFactoryPeer::GetActiveSession(factory_.get(), server_id,
                                                    network_isolation_key);
   }
 
-  int GetSourcePortForNewSession(const HostPortPair& destination) {
+  int GetSourcePortForNewSession(const url::SchemeHostPort& destination) {
     return GetSourcePortForNewSessionInner(destination, false);
   }
 
-  int GetSourcePortForNewSessionAndGoAway(const HostPortPair& destination) {
+  int GetSourcePortForNewSessionAndGoAway(
+      const url::SchemeHostPort& destination) {
     return GetSourcePortForNewSessionInner(destination, true);
   }
 
-  int GetSourcePortForNewSessionInner(const HostPortPair& destination,
+  int GetSourcePortForNewSessionInner(const url::SchemeHostPort& destination,
                                       bool goaway_received) {
     // Should only be called if there is no active session for this destination.
     EXPECT_FALSE(HasActiveSession(destination));
@@ -531,7 +537,7 @@
     QuicStreamRequest request(factory_.get());
     EXPECT_EQ(ERR_IO_PENDING,
               request.Request(
-                  host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                  scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                   SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                   true /* use_dns_aliases */,
                   /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -556,9 +562,9 @@
               stream->InitializeStream(&request_info, true, DEFAULT_PRIORITY,
                                        net_log_, CompletionOnceCallback()));
     // Ensure that session is alive and active.
-    QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+    QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
     EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-    EXPECT_TRUE(HasActiveSession(host_port_pair_));
+    EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
     IPEndPoint actual_address;
     session->GetDefaultSocket()->GetPeerAddress(&actual_address);
@@ -614,7 +620,7 @@
     QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), runner_.get());
 
     const AlternativeService alternative_service1(
-        kProtoQUIC, host_port_pair_.host(), host_port_pair_.port());
+        kProtoQUIC, scheme_host_port_.host(), scheme_host_port_.port());
     AlternativeServiceInfoVector alternative_service_info_vector;
     base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
     alternative_service_info_vector.push_back(
@@ -732,7 +738,7 @@
     ++quic_server_info_map_it;
     EXPECT_EQ(quic_server_info_map_it->first.server_id, quic_server_id1);
 
-    host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+    host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                               "192.168.0.1", "");
 
     // Create a session and verify that the cached state is loaded.
@@ -746,7 +752,8 @@
     QuicStreamRequest request(factory_.get());
     EXPECT_EQ(ERR_IO_PENDING,
               request.Request(
-                  HostPortPair(quic_server_id1.host(), quic_server_id1.port()),
+                  url::SchemeHostPort(url::kHttpsScheme, quic_server_id1.host(),
+                                      quic_server_id1.port()),
                   version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
                   network_isolation_key1, SecureDnsPolicy::kAllow,
                   true /* use_dns_aliases */, /*cert_verify_flags=*/0, url_,
@@ -782,14 +789,15 @@
       socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
     socket_data2.AddSocketDataToFactory(socket_factory_.get());
 
-    host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+    host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                               "192.168.0.2", "");
 
     QuicStreamRequest request2(factory_.get());
     EXPECT_EQ(
         ERR_IO_PENDING,
         request2.Request(
-            HostPortPair(quic_server_id2.host(), quic_server_id2.port()),
+            url::SchemeHostPort(url::kHttpsScheme, quic_server_id2.host(),
+                                quic_server_id2.port()),
             version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
             network_isolation_key2, SecureDnsPolicy::kAllow,
             true /* use_dns_aliases */, /*cert_verify_flags=*/0,
@@ -932,7 +940,7 @@
   std::unique_ptr<ScopedMockNetworkChangeNotifier>
       scoped_mock_network_change_notifier_;
   std::unique_ptr<QuicStreamFactory> factory_;
-  HostPortPair host_port_pair_;
+  url::SchemeHostPort scheme_host_port_;
   GURL url_;
   GURL url2_;
   GURL url3_;
@@ -976,7 +984,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -991,7 +999,7 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -1006,7 +1014,7 @@
   QuicStreamRequest request3(factory_.get());
   EXPECT_EQ(OK,
             request3.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -1034,13 +1042,13 @@
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::ZERO_RTT);
   host_resolver_->set_synchronous_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             "192.168.0.1", "");
 
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -1073,22 +1081,22 @@
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::ASYNC_ZERO_RTT);
   host_resolver_->set_synchronous_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             "192.168.0.1", "");
 
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(nullptr, CreateStream(&request));
 
   crypto_client_stream_factory_.last_stream()->NotifySessionZeroRttComplete();
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
 
@@ -1112,7 +1120,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -1122,7 +1130,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(session->require_confirmation());
   EXPECT_EQ(100000u, session->connection()->GetStats().srtt_us);
   ASSERT_FALSE(session->config()->HasInitialRoundTripTimeUsToSend());
@@ -1142,13 +1150,13 @@
   auto request = std::make_unique<QuicStreamRequest>(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request->Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   request.reset();
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
   // Tearing down a QuicStreamFactory with a pending Job should not cause any
   // crash. crbug.com/768343.
   factory_.reset();
@@ -1158,7 +1166,7 @@
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::ZERO_RTT);
   host_resolver_->set_synchronous_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             "192.168.0.1", "");
   Initialize();
   factory_->set_is_quic_known_to_work_on_current_network(false);
@@ -1175,7 +1183,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -1192,7 +1200,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(session->require_confirmation());
 }
 
@@ -1200,7 +1208,7 @@
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::ZERO_RTT);
   host_resolver_->set_synchronous_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             "192.168.0.1", "");
   Initialize();
   factory_->set_is_quic_known_to_work_on_current_network(false);
@@ -1219,7 +1227,7 @@
 
   QuicStreamRequest request(factory_.get());
   EXPECT_THAT(request.Request(
-                  host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                  scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                   SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                   true /* use_dns_aliases */,
                   /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -1231,7 +1239,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_FALSE(session->require_confirmation());
 
   crypto_client_stream_factory_.last_stream()
@@ -1260,7 +1268,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -1270,7 +1278,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_EQ(10000u, session->connection()->GetStats().srtt_us);
   ASSERT_TRUE(session->config()->HasInitialRoundTripTimeUsToSend());
   EXPECT_EQ(10000u, session->config()->GetInitialRoundTripTimeUsToSend());
@@ -1328,7 +1336,7 @@
     QuicStreamRequest request(factory_.get());
     EXPECT_EQ(ERR_IO_PENDING,
               request.Request(
-                  host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                  scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                   SocketTag(), network_isolation_key, SecureDnsPolicy::kAllow,
                   true /* use_dns_aliases */,
                   /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -1339,7 +1347,7 @@
     EXPECT_TRUE(stream.get());
 
     QuicChromiumClientSession* session =
-        GetActiveSession(host_port_pair_, network_isolation_key);
+        GetActiveSession(scheme_host_port_, network_isolation_key);
     if (network_isolation_key == kNetworkIsolationKey1) {
       EXPECT_EQ(10000, session->connection()->GetStats().srtt_us);
       ASSERT_TRUE(session->config()->HasInitialRoundTripTimeUsToSend());
@@ -1371,7 +1379,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -1381,7 +1389,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_EQ(1000000u, session->connection()->GetStats().srtt_us);
   ASSERT_TRUE(session->config()->HasInitialRoundTripTimeUsToSend());
   EXPECT_EQ(1200000u, session->config()->GetInitialRoundTripTimeUsToSend());
@@ -1406,7 +1414,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -1416,7 +1424,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_EQ(400000u, session->connection()->GetStats().srtt_us);
   ASSERT_TRUE(session->config()->HasInitialRoundTripTimeUsToSend());
   EXPECT_EQ(400000u, session->config()->GetInitialRoundTripTimeUsToSend());
@@ -1436,7 +1444,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -1446,7 +1454,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
 
   if (version_.UsesHttp3()) {
     session->OnHttp3GoAway(0);
@@ -1454,7 +1462,7 @@
     session->OnGoAway(quic::QuicGoAwayFrame());
   }
 
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
 
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
@@ -1479,7 +1487,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -1489,7 +1497,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
 
   session->OnGoAway(quic::QuicGoAwayFrame(
       quic::kInvalidControlFrameId, quic::QUIC_ERROR_MIGRATING_PORT, 0,
@@ -1502,7 +1510,7 @@
   stream->PopulateNetErrorDetails(&details);
   EXPECT_TRUE(details.quic_port_migration_detected);
 
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
 
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
@@ -1559,7 +1567,7 @@
     QuicStreamRequest request(factory_.get());
     EXPECT_EQ(ERR_IO_PENDING,
               request.Request(
-                  host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                  scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                   SocketTag(), kNetworkIsolationKeys[i],
                   SecureDnsPolicy::kAllow, true /* use_dns_aliases */,
                   /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -1570,7 +1578,7 @@
     EXPECT_TRUE(stream.get());
 
     QuicChromiumClientSession* session =
-        GetActiveSession(host_port_pair_, kNetworkIsolationKeys[i]);
+        GetActiveSession(scheme_host_port_, kNetworkIsolationKeys[i]);
 
     if (version_.UsesHttp3()) {
       session->OnHttp3GoAway(0);
@@ -1578,7 +1586,7 @@
       session->OnGoAway(quic::QuicGoAwayFrame());
     }
 
-    EXPECT_FALSE(HasActiveSession(host_port_pair_, kNetworkIsolationKeys[i]));
+    EXPECT_FALSE(HasActiveSession(scheme_host_port_, kNetworkIsolationKeys[i]));
 
     EXPECT_TRUE(socket_data.AllReadDataConsumed());
     EXPECT_TRUE(socket_data.AllWriteDataConsumed());
@@ -1616,7 +1624,7 @@
     QuicStreamRequest request(factory_.get());
     EXPECT_EQ(ERR_IO_PENDING,
               request.Request(
-                  host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                  scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                   SocketTag(), kNetworkIsolationKeys[i],
                   SecureDnsPolicy::kAllow, true /* use_dns_aliases */,
                   /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -1624,7 +1632,7 @@
 
     EXPECT_THAT(callback_.WaitForResult(), IsError(ERR_QUIC_HANDSHAKE_FAILED));
 
-    EXPECT_FALSE(HasActiveSession(host_port_pair_, kNetworkIsolationKeys[i]));
+    EXPECT_FALSE(HasActiveSession(scheme_host_port_, kNetworkIsolationKeys[i]));
 
     for (size_t j = 0; j < base::size(kNetworkIsolationKeys); ++j) {
       // Stats up to kNetworkIsolationKeys[j] should have been deleted, all
@@ -1651,16 +1659,17 @@
     socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
-  HostPortPair server2(kServer2HostName, kDefaultServerPort);
+  url::SchemeHostPort server2(url::kHttpsScheme, kServer2HostName,
+                              kDefaultServerPort);
   host_resolver_->set_synchronous_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             "192.168.0.1", "");
   host_resolver_->rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
 
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -1679,7 +1688,7 @@
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
   EXPECT_TRUE(stream2.get());
 
-  EXPECT_EQ(GetActiveSession(host_port_pair_), GetActiveSession(server2));
+  EXPECT_EQ(GetActiveSession(scheme_host_port_), GetActiveSession(server2));
 
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
@@ -1688,7 +1697,7 @@
 // Regression test for https://crbug.com/639916.
 TEST_P(QuicStreamFactoryTest, PoolingWithServerMigration) {
   // Set up session to migrate.
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             "192.168.0.1", "");
   IPEndPoint alt_address = IPEndPoint(IPAddress(1, 2, 3, 4), 443);
   quic::QuicConfig config;
@@ -1706,14 +1715,15 @@
   VerifyServerMigration(config, alt_address);
 
   // Close server-migrated session.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   session->CloseSessionOnError(0u, quic::QUIC_NO_ERROR,
                                quic::ConnectionCloseBehavior::SILENT_CLOSE);
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
 
   client_maker_.Reset();
   // Set up server IP, socket, proof, and config for new session.
-  HostPortPair server2(kServer2HostName, kDefaultServerPort);
+  url::SchemeHostPort server2(url::kHttpsScheme, kServer2HostName,
+                              kDefaultServerPort);
   host_resolver_->rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
 
   MockQuicData socket_data1(version_);
@@ -1749,7 +1759,7 @@
   EXPECT_TRUE(HasActiveSession(server2));
 
   // No zombie entry in session map.
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
 }
 
 TEST_P(QuicStreamFactoryTest, NoPoolingAfterGoAway) {
@@ -1770,16 +1780,17 @@
     socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data2.AddSocketDataToFactory(socket_factory_.get());
 
-  HostPortPair server2(kServer2HostName, kDefaultServerPort);
+  url::SchemeHostPort server2(url::kHttpsScheme, kServer2HostName,
+                              kDefaultServerPort);
   host_resolver_->set_synchronous_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             "192.168.0.1", "");
   host_resolver_->rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
 
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -1798,8 +1809,8 @@
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
   EXPECT_TRUE(stream2.get());
 
-  factory_->OnSessionGoingAway(GetActiveSession(host_port_pair_));
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
+  factory_->OnSessionGoingAway(GetActiveSession(scheme_host_port_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
   EXPECT_FALSE(HasActiveSession(server2));
 
   TestCompletionCallback callback3;
@@ -1830,8 +1841,8 @@
     socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
-  HostPortPair server1(kDefaultServerHostName, 443);
-  HostPortPair server2(kServer2HostName, 443);
+  url::SchemeHostPort server1(url::kHttpsScheme, kDefaultServerHostName, 443);
+  url::SchemeHostPort server2(url::kHttpsScheme, kServer2HostName, 443);
 
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
@@ -1875,8 +1886,8 @@
     socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
-  HostPortPair server1(kDefaultServerHostName, 443);
-  HostPortPair server2(kServer2HostName, 443);
+  url::SchemeHostPort server1(url::kHttpsScheme, kDefaultServerHostName, 443);
+  url::SchemeHostPort server2(url::kHttpsScheme, kServer2HostName, 443);
   transport_security_state_.EnableStaticPinsForTesting();
   ScopedTransportSecurityStateSource scoped_security_state_source;
 
@@ -1933,8 +1944,8 @@
     socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data2.AddSocketDataToFactory(socket_factory_.get());
 
-  HostPortPair server1(kDefaultServerHostName, 443);
-  HostPortPair server2(kServer2HostName, 443);
+  url::SchemeHostPort server1(url::kHttpsScheme, kDefaultServerHostName, 443);
+  url::SchemeHostPort server2(url::kHttpsScheme, kServer2HostName, 443);
   transport_security_state_.EnableStaticPinsForTesting();
   ScopedTransportSecurityStateSource scoped_security_state_source;
 
@@ -2005,7 +2016,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -2017,18 +2028,18 @@
 
   // Mark the session as going away.  Ensure that while it is still alive
   // that it is no longer active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   factory_->OnSessionGoingAway(session);
   EXPECT_EQ(true,
             QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
 
   // Create a new request for the same destination and verify that a
   // new session is created.
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -2037,8 +2048,8 @@
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
   EXPECT_TRUE(stream2.get());
 
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
-  EXPECT_NE(session, GetActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
+  EXPECT_NE(session, GetActiveSession(scheme_host_port_));
   EXPECT_EQ(true,
             QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
 
@@ -2099,8 +2110,8 @@
   for (size_t i = 0; i < quic::kDefaultMaxStreamsPerConnection / 2; i++) {
     QuicStreamRequest request(factory_.get());
     int rv = request.Request(
-        host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
-        NetworkIsolationKey(), SecureDnsPolicy::kAllow,
+        scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
+        SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
         true /* use_dns_aliases */,
         /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
         failed_on_default_network_callback_, callback_.callback());
@@ -2121,7 +2132,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -2145,7 +2156,7 @@
   // Force close of the connection to suppress the generation of RST
   // packets when streams are torn down, which wouldn't be relevant to
   // this test anyway.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   session->connection()->CloseConnection(
       quic::QUIC_PUBLIC_RESET, "test",
       quic::ConnectionCloseBehavior::SILENT_CLOSE);
@@ -2161,7 +2172,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -2183,7 +2194,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -2206,7 +2217,7 @@
     QuicStreamRequest request(factory_.get());
     EXPECT_EQ(ERR_IO_PENDING,
               request.Request(
-                  host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                  scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                   SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                   true /* use_dns_aliases */,
                   /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -2218,7 +2229,7 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -2261,7 +2272,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -2287,7 +2298,7 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -2324,14 +2335,14 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(ERR_QUIC_HANDSHAKE_FAILED, callback_.WaitForResult());
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
-  EXPECT_FALSE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
+  EXPECT_FALSE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // Verify new requests can be sent normally without hanging.
   crypto_client_stream_factory_.set_handshake_mode(
@@ -2348,13 +2359,13 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
   // Run the message loop to complete host resolution.
   base::RunLoop().RunUntilIdle();
 
@@ -2362,8 +2373,8 @@
   crypto_client_stream_factory_.last_stream()
       ->NotifySessionOneRttKeyAvailable();
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
-  EXPECT_FALSE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
+  EXPECT_FALSE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // Create QuicHttpStream.
   std::unique_ptr<HttpStream> stream = CreateStream(&request2);
@@ -2381,7 +2392,7 @@
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::COLD_START_WITH_CHLO_SENT);
   host_resolver_->set_synchronous_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             "192.168.0.1", "");
 
   MockQuicData socket_data(version_);
@@ -2394,14 +2405,14 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_QUIC_HANDSHAKE_FAILED,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   // Check no active session, or active jobs left for this server.
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
-  EXPECT_FALSE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
+  EXPECT_FALSE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // Verify new requests can be sent normally without hanging.
   crypto_client_stream_factory_.set_handshake_mode(
@@ -2418,20 +2429,20 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // Complete handshake.
   crypto_client_stream_factory_.last_stream()
       ->NotifySessionOneRttKeyAvailable();
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
-  EXPECT_FALSE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
+  EXPECT_FALSE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // Create QuicHttpStream.
   std::unique_ptr<HttpStream> stream = CreateStream(&request2);
@@ -2473,7 +2484,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -2488,8 +2499,8 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Check an active session exists for the destination.
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
 
   EXPECT_TRUE(http_server_properties_->HasLastLocalAddressWhenQuicWorked());
@@ -2501,14 +2512,14 @@
   EXPECT_FALSE(factory_->is_quic_known_to_work_on_current_network());
   EXPECT_FALSE(http_server_properties_->HasLastLocalAddressWhenQuicWorked());
   // Check no active session exists for the destination.
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
 
   // Now attempting to request a stream to the same origin should create
   // a new session.
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -2519,8 +2530,8 @@
 
   // Check a new active session exists for the destination and the old session
   // is no longer live.
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
-  QuicChromiumClientSession* session2 = GetActiveSession(host_port_pair_);
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
+  QuicChromiumClientSession* session2 = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session2));
 
   stream.reset();  // Will reset stream 3.
@@ -2570,7 +2581,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -2589,9 +2600,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Send GET request on stream.
   HttpResponseInfo response;
@@ -2603,7 +2614,7 @@
   NotifyIPAddressChanged();
 
   // The connection should still be alive, but marked as going away.
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
@@ -2617,7 +2628,7 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -2627,9 +2638,9 @@
   EXPECT_TRUE(stream2.get());
 
   // Check an active session exists for the destination.
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  QuicChromiumClientSession* session2 = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session2 = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session2));
 
   stream.reset();
@@ -2666,7 +2677,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -2691,7 +2702,7 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -2797,7 +2808,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -2816,9 +2827,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Send GET request on stream.
@@ -2865,7 +2876,7 @@
 
   // The connection should still be alive, and not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
 
@@ -2875,7 +2886,7 @@
   quic_data2.Resume();
 
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // There should be three pending tasks for gQUIC and 2 for IETF QUIC, the
@@ -2915,7 +2926,7 @@
 
   // Verify that the session is still alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   stream.reset();
   EXPECT_TRUE(quic_data1.AllReadDataConsumed());
@@ -3019,7 +3030,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -3038,9 +3049,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Send GET request on stream.
@@ -3099,7 +3110,7 @@
 
   // The connection should still be alive, and not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
 
@@ -3145,7 +3156,7 @@
 
   // Verify that the session is still alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   stream.reset();
   EXPECT_TRUE(quic_data1.AllReadDataConsumed());
@@ -3180,7 +3191,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -3197,9 +3208,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Trigger connection migration. Since there are no networks
   // to migrate to, this should cause the session to wait for a new network.
@@ -3208,7 +3219,7 @@
 
   // The migration will not fail until the migration alarm timeout.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
   EXPECT_EQ(true, session->connection()->writer()->IsWriteBlocked());
@@ -3223,7 +3234,7 @@
   // The connection should now be closed. A request for response
   // headers should fail.
   EXPECT_FALSE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(ERR_INTERNET_DISCONNECTED, callback_.WaitForResult());
 
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
@@ -3364,7 +3375,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -3382,9 +3393,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Trigger connection migration. Session will start to probe the alternative
@@ -3394,14 +3405,14 @@
       ->NotifyNetworkMadeDefault(kNewNetworkForTests);
 
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Resume data to read a connectivity probing response, which will cause
   // non-migtable streams to be closed.
   quic_data1.Resume();
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_EQ(migrate_idle_sessions, HasActiveSession(host_port_pair_));
+  EXPECT_EQ(migrate_idle_sessions, HasActiveSession(scheme_host_port_));
   EXPECT_EQ(0u, session->GetNumActiveStreams());
 
   base::RunLoop().RunUntilIdle();
@@ -3440,7 +3451,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -3457,9 +3468,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Set session config to have connection migration disabled.
   quic::test::QuicConfigPeer::SetReceivedDisableConnectionMigration(
@@ -3472,7 +3483,7 @@
       ->NotifyNetworkMadeDefault(kNewNetworkForTests);
 
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   stream.reset();
@@ -3573,7 +3584,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -3591,9 +3602,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Trigger connection migration. Since there is a non-migratable stream,
@@ -3606,7 +3617,7 @@
 
   EXPECT_EQ(migrate_idle_sessions,
             QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_EQ(migrate_idle_sessions, HasActiveSession(host_port_pair_));
+  EXPECT_EQ(migrate_idle_sessions, HasActiveSession(scheme_host_port_));
 
   if (migrate_idle_sessions) {
     EXPECT_EQ(0u, session->GetNumActiveStreams());
@@ -3639,7 +3650,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -3656,9 +3667,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Set session config to have connection migration disabled.
   quic::test::QuicConfigPeer::SetReceivedDisableConnectionMigration(
@@ -3670,7 +3681,7 @@
       ->NotifyNetworkDisconnected(kDefaultNetworkForTests);
 
   EXPECT_FALSE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
 
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
@@ -3748,7 +3759,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -3758,16 +3769,16 @@
   EXPECT_TRUE(stream.get());
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_FALSE(session->HasActiveRequestStreams());
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Trigger connection migration.
   scoped_mock_network_change_notifier_->mock_network_change_notifier()
       ->NotifyNetworkMadeDefault(kNewNetworkForTests);
-  EXPECT_EQ(migrate_idle_sessions, HasActiveSession(host_port_pair_));
+  EXPECT_EQ(migrate_idle_sessions, HasActiveSession(scheme_host_port_));
 
   if (migrate_idle_sessions) {
     quic_data1.Resume();
@@ -3841,7 +3852,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -3851,7 +3862,7 @@
   EXPECT_TRUE(stream.get());
 
   // Ensure that session is active.
-  auto* session = GetActiveSession(host_port_pair_);
+  auto* session = GetActiveSession(scheme_host_port_);
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Trigger connection migration. Since there are no active streams,
@@ -3859,7 +3870,7 @@
   scoped_mock_network_change_notifier_->mock_network_change_notifier()
       ->NotifyNetworkDisconnected(kDefaultNetworkForTests);
 
-  EXPECT_EQ(migrate_idle_sessions, HasActiveSession(host_port_pair_));
+  EXPECT_EQ(migrate_idle_sessions, HasActiveSession(scheme_host_port_));
 
   EXPECT_TRUE(default_socket_data.AllReadDataConsumed());
   EXPECT_TRUE(default_socket_data.AllWriteDataConsumed());
@@ -3917,7 +3928,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -3936,9 +3947,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   quic::QuicConnectionId cid_on_new_path =
       quic::test::TestConnectionId(12345678);
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
@@ -4004,13 +4015,13 @@
 
   // The connection should still be alive, not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
 
   // Ensure that the session is still alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Run the message loop so that data queued in the new socket is read by the
@@ -4023,7 +4034,7 @@
 
   // Check that the session is still alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // There should be posted tasks not executed, which is to migrate back to
   // default network.
@@ -4072,7 +4083,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -4091,9 +4102,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   quic::QuicConnectionId cid_on_new_path =
       quic::test::TestConnectionId(12345678);
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
@@ -4111,7 +4122,7 @@
 
   // The connection should still be alive, not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
 
@@ -4169,7 +4180,7 @@
 
   // Ensure that the session is still alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Run the message loop so that data queued in the new socket is read by the
@@ -4182,7 +4193,7 @@
 
   // Check that the session is still alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // There should posted tasks not executed, which is to migrate back to default
   // network.
@@ -4296,7 +4307,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -4315,9 +4326,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Send GET request on stream.
@@ -4344,7 +4355,7 @@
 
   // The connection should still be alive, and not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
 
@@ -4353,7 +4364,7 @@
   quic_data2.Resume();
 
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // There should be three pending tasks, the nearest one will complete
@@ -4402,7 +4413,7 @@
 
   // Verify that the session is still alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   stream.reset();
   EXPECT_TRUE(quic_data1.AllReadDataConsumed());
@@ -4515,7 +4526,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -4534,9 +4545,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Send GET request on stream.
@@ -4568,7 +4579,7 @@
 
   // The connection should still be alive, and not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
 
@@ -4577,7 +4588,7 @@
   quic_data2.Resume();
 
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // There should be three pending tasks, the nearest one will complete
@@ -4631,7 +4642,7 @@
 
   // Verify that the session is still alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   stream.reset();
   EXPECT_TRUE(quic_data1.AllReadDataConsumed());
@@ -4705,7 +4716,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -4724,9 +4735,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Send GET request on stream.
@@ -4745,7 +4756,7 @@
 
   // The connection should still be alive, and not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
 
@@ -4770,7 +4781,7 @@
 
   // Verify that the session is still alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   stream.reset();
   EXPECT_TRUE(quic_data1.AllReadDataConsumed());
@@ -4850,7 +4861,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -4869,9 +4880,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_path2, session);
 
   // Send GET request on stream.
@@ -4890,7 +4901,7 @@
 
   // The connection should still be alive, and not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
 
@@ -4919,7 +4930,7 @@
 
   // Verify that the session is still alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   stream.reset();
   EXPECT_TRUE(quic_data1.AllReadDataConsumed());
@@ -5024,7 +5035,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -5043,9 +5054,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Send GET request on stream.
   HttpResponseInfo response;
@@ -5070,7 +5081,7 @@
 
   // The connection should still be alive, and not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
 
@@ -5081,7 +5092,7 @@
 
   // Verify that the session is still active, and the request stream is active.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_FALSE(
       QuicChromiumClientSessionPeer::DoesSessionAllowPortMigration(session));
@@ -5155,7 +5166,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -5174,9 +5185,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Send GET request on stream.
@@ -5202,7 +5213,7 @@
 
   // The connection should still be alive, and not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
 
@@ -5213,7 +5224,7 @@
 
   // Verify that the session is still active, and the request stream is active.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   stream.reset();
@@ -5414,7 +5425,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -5433,9 +5444,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Send GET request on stream.
@@ -5482,7 +5493,7 @@
 
   // The connection should still be alive, and not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
 
@@ -5491,7 +5502,7 @@
   quic_data2.Resume();
 
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   // Successful port migration causes the path no longer degrading on the same
   // network.
@@ -5529,7 +5540,7 @@
   // Verify that the session is still alive, and the request stream is still
   // alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   chrome_stream = static_cast<QuicChromiumClientStream*>(
       quic::test::QuicSessionPeer::GetStream(
           session, GetNthClientInitiatedBidirectionalStreamId(0)));
@@ -5576,7 +5587,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -5595,9 +5606,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Send GET request on stream.
   HttpResponseInfo response;
@@ -5678,7 +5689,7 @@
 
     // The connection should still be alive, and not marked as going away.
     EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-    EXPECT_TRUE(HasActiveSession(host_port_pair_));
+    EXPECT_TRUE(HasActiveSession(scheme_host_port_));
     EXPECT_EQ(1u, session->GetNumActiveStreams());
 
     // Resume quic data and a connectivity probe response will be read on the
@@ -5687,7 +5698,7 @@
     base::RunLoop().RunUntilIdle();
 
     EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-    EXPECT_TRUE(HasActiveSession(host_port_pair_));
+    EXPECT_TRUE(HasActiveSession(scheme_host_port_));
     EXPECT_EQ(1u, session->GetNumActiveStreams());
 
     if (i < 4) {
@@ -5714,7 +5725,7 @@
 
   // Verify that the session is still alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   stream.reset();
   EXPECT_TRUE(quic_data1.AllReadDataConsumed());
@@ -5760,7 +5771,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -5779,9 +5790,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Send GET request on stream.
   HttpResponseInfo response;
@@ -5800,7 +5811,7 @@
   EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get()));
 
   // The connection should still be alive, but marked as going away.
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
@@ -5808,7 +5819,7 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -5825,9 +5836,9 @@
   EXPECT_EQ(0U, session->GetNumActiveStreams());
 
   // Check an active session exists for the destination.
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  QuicChromiumClientSession* session2 = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session2 = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session2));
   EXPECT_NE(session, session2);
 
@@ -5901,7 +5912,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -5920,9 +5931,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Send GET request on stream.
   HttpResponseInfo response;
@@ -5938,7 +5949,7 @@
 
   // The connection should still be alive, and not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
 
@@ -5952,7 +5963,7 @@
 
   // Verify that the session is still alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   stream.reset();
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
@@ -6057,7 +6068,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -6076,9 +6087,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Send GET request on stream.
@@ -6096,7 +6107,7 @@
   // Cause the connection to report path degrading to the session.
   // Session should still start to probe the alternate network.
   session->connection()->OnPathDegradingDetected();
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get()));
 
   base::TimeDelta next_task_delay;
@@ -6117,7 +6128,7 @@
   quic_data2.Resume();
 
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(0u, session->GetNumActiveStreams());
   EXPECT_TRUE(session->HasActiveRequestStreams());
 
@@ -6161,7 +6172,7 @@
 
   // Verify that the session is still alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(OK, stream->ReadResponseHeaders(callback_.callback()));
 
   stream.reset();
@@ -6257,7 +6268,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -6276,9 +6287,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Send GET request on stream.
@@ -6319,7 +6330,7 @@
 
   // The connection should still be alive, and not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
 
@@ -6328,7 +6339,7 @@
   quic_data2.Resume();
 
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // There should be three pending tasks, the nearest one will complete
@@ -6377,7 +6388,7 @@
 
   // Verify that the session is still alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   stream.reset();
   EXPECT_TRUE(quic_data1.AllReadDataConsumed());
@@ -6406,8 +6417,8 @@
   socket_data2.AddWrite(ASYNC, OK);
   socket_data2.AddSocketDataToFactory(socket_factory_.get());
 
-  HostPortPair server1(kDefaultServerHostName, 443);
-  HostPortPair server2(kServer2HostName, 443);
+  url::SchemeHostPort server1(url::kHttpsScheme, kDefaultServerHostName, 443);
+  url::SchemeHostPort server2(url::kHttpsScheme, kServer2HostName, 443);
 
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
@@ -6562,7 +6573,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -6581,9 +6592,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Send GET request on stream.
   HttpResponseInfo response;
@@ -6599,7 +6610,7 @@
   EXPECT_TRUE(session->connection()->IsPathDegrading());
   EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get()));
 
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
@@ -6608,7 +6619,7 @@
   quic_data.Resume();
 
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   stream.reset();
@@ -6746,7 +6757,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -6764,9 +6775,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Trigger connection migration. Since there is a non-migratable stream,
@@ -6777,7 +6788,7 @@
   // packet reader.
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Resume the data to read the connectivity probing response to declare probe
@@ -6786,7 +6797,7 @@
   if (migrate_idle_sessions)
     base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(migrate_idle_sessions, HasActiveSession(host_port_pair_));
+  EXPECT_EQ(migrate_idle_sessions, HasActiveSession(scheme_host_port_));
   EXPECT_EQ(0u, session->GetNumActiveStreams());
 
   EXPECT_TRUE(quic_data1.AllReadDataConsumed());
@@ -6825,7 +6836,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -6842,9 +6853,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Set session config to have connection migration disabled.
   quic::test::QuicConfigPeer::SetReceivedDisableConnectionMigration(
@@ -6860,7 +6871,7 @@
   base::RunLoop().RunUntilIdle();
 
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   stream.reset();
@@ -6984,7 +6995,7 @@
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request1.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -7007,7 +7018,7 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -7025,9 +7036,9 @@
                                       net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(2u, session->GetNumActiveStreams());
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
@@ -7058,7 +7069,7 @@
 
   // Verify the session is still alive and not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(2u, session->GetNumActiveStreams());
   // There should be one task posted to migrate back to the default network in
   // kMinRetryTimeForDefaultNetworkSecs.
@@ -7160,7 +7171,7 @@
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request1.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -7179,9 +7190,9 @@
                                       net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   MaybeMakeNewConnectionIdAvailableToSession(cid1, session);
 
@@ -7205,7 +7216,7 @@
 
   // Verify the session is still alive and not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   // There should be one task posted to migrate back to the default network in
   // kMinRetryTimeForDefaultNetworkSecs.
@@ -7262,7 +7273,7 @@
 
   // Verify the session is still alive and not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // There should be one task posted to one will resend a connectivity probe and
@@ -7307,7 +7318,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -7316,9 +7327,9 @@
   base::RunLoop().RunUntilIdle();
 
   // Ensure that session is alive but not active.
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
-  QuicChromiumClientSession* session = GetPendingSession(host_port_pair_);
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
+  QuicChromiumClientSession* session = GetPendingSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
   EXPECT_EQ(0u, task_runner->GetPendingTaskCount());
 
@@ -7329,8 +7340,8 @@
   EXPECT_EQ(0u, task_runner->GetPendingTaskCount());
   EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get()));
 
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
 }
@@ -7371,7 +7382,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -7380,9 +7391,9 @@
   base::RunLoop().RunUntilIdle();
 
   // Ensure that session is alive but not active.
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
-  QuicChromiumClientSession* session = GetPendingSession(host_port_pair_);
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
+  QuicChromiumClientSession* session = GetPendingSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
   EXPECT_EQ(0u, task_runner->GetPendingTaskCount());
 
@@ -7392,8 +7403,8 @@
   session->connection()->OnPathDegradingDetected();
   EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get()));
   EXPECT_EQ(0u, task_runner->GetPendingTaskCount());
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // Cause the connection to close due to |quic_error| before handshake.
   std::string error_details;
@@ -7410,8 +7421,8 @@
   task_runner->FastForwardUntilNoTasksRemain();
 
   // No new session should be created as there is no alternate network.
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
-  EXPECT_FALSE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
+  EXPECT_FALSE(HasActiveJob(scheme_host_port_, privacy_mode_));
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
 }
@@ -7524,7 +7535,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -7533,9 +7544,9 @@
   base::RunLoop().RunUntilIdle();
 
   // Ensure that session is alive but not active.
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
-  QuicChromiumClientSession* session = GetPendingSession(host_port_pair_);
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
+  QuicChromiumClientSession* session = GetPendingSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
   EXPECT_EQ(0u, task_runner->GetPendingTaskCount());
   EXPECT_FALSE(failed_on_default_network_);
@@ -7554,9 +7565,9 @@
   task_runner->FastForwardUntilNoTasksRemain();
 
   // Verify a new session is created on the alternate network.
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
-  QuicChromiumClientSession* session2 = GetPendingSession(host_port_pair_);
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
+  QuicChromiumClientSession* session2 = GetPendingSession(scheme_host_port_);
   EXPECT_NE(session, session2);
   EXPECT_TRUE(failed_on_default_network_);
 
@@ -7564,7 +7575,7 @@
   crypto_client_stream_factory_.last_stream()
       ->NotifySessionOneRttKeyAvailable();
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_path1, session2);
   // Resume the data now so that data can be sent and read.
   socket_data2.Resume();
@@ -7645,14 +7656,14 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(ERR_QUIC_HANDSHAKE_FAILED, callback_.WaitForResult());
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
-  EXPECT_FALSE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
+  EXPECT_FALSE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // Verify new requests can be sent normally.
   crypto_client_stream_factory_.set_handshake_mode(
@@ -7669,13 +7680,13 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
   // Run the message loop to complete host resolution.
   base::RunLoop().RunUntilIdle();
 
@@ -7683,8 +7694,8 @@
   crypto_client_stream_factory_.last_stream()
       ->NotifySessionOneRttKeyAvailable();
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
-  EXPECT_FALSE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
+  EXPECT_FALSE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // Create QuicHttpStream.
   std::unique_ptr<HttpStream> stream = CreateStream(&request2);
@@ -7758,23 +7769,23 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   // Ensure that the session is alive but not active.
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
   base::RunLoop().RunUntilIdle();
-  QuicChromiumClientSession* session = GetPendingSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetPendingSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
 
   // Confirm the handshake on the alternate network.
   crypto_client_stream_factory_.last_stream()
       ->NotifySessionOneRttKeyAvailable();
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Resume the data now so that data can be sent and read.
   socket_data2.Resume();
@@ -7832,7 +7843,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -7851,9 +7862,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   quic::QuicConnectionId cid_on_new_path =
       quic::test::TestConnectionId(12345678);
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
@@ -7925,7 +7936,7 @@
 
   // Verify that session is alive and not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Verify that response headers on the migrated socket were delivered to the
@@ -7969,7 +7980,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -7988,9 +7999,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Send GET request on stream. This causes a write error, which triggers
   // a connection migration attempt. Since there are no networks
@@ -8010,13 +8021,13 @@
 
   // Migration has not yet failed. The session should be alive and active.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_TRUE(session->connection()->writer()->IsWriteBlocked());
 
   // The migration will not fail until the migration alarm timeout.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
 
@@ -8026,7 +8037,7 @@
   // The connection should be closed. A request for response headers
   // should fail.
   EXPECT_FALSE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(ERR_NETWORK_CHANGED, callback_.WaitForResult());
   EXPECT_EQ(ERR_NETWORK_CHANGED,
             stream->ReadResponseHeaders(callback_.callback()));
@@ -8157,7 +8168,7 @@
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request1.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -8180,7 +8191,7 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -8197,9 +8208,9 @@
                                       net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(2u, session->GetNumActiveStreams());
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
@@ -8216,7 +8227,7 @@
 
   // Verify session is still alive and not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(2u, session->GetNumActiveStreams());
 
   // Verify that response headers on the migrated socket were delivered to the
@@ -8339,7 +8350,7 @@
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request1.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -8362,7 +8373,7 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -8381,9 +8392,9 @@
                                       net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(2u, session->GetNumActiveStreams());
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
@@ -8401,7 +8412,7 @@
   // Verify that the session is still alive and not marked as going away.
   // Non-migratable stream should be closed due to migration.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Verify that response headers on the migrated socket were delivered to the
@@ -8531,7 +8542,7 @@
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request1.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -8554,7 +8565,7 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -8573,9 +8584,9 @@
                                       net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(2u, session->GetNumActiveStreams());
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
@@ -8592,7 +8603,7 @@
   // closed.
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Send GET request on stream 1.
@@ -8703,7 +8714,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -8723,9 +8734,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Send GET request on stream. This should cause a write error, which triggers
@@ -8743,7 +8754,7 @@
   // successfully; otherwise the connection is closed.
   EXPECT_EQ(migrate_idle_sessions,
             QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_EQ(migrate_idle_sessions, HasActiveSession(host_port_pair_));
+  EXPECT_EQ(migrate_idle_sessions, HasActiveSession(scheme_host_port_));
 
   if (migrate_idle_sessions) {
     EXPECT_TRUE(failed_socket_data.AllReadDataConsumed());
@@ -8793,7 +8804,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -8812,9 +8823,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Set session config to have connection migration disabled.
   quic::test::QuicConfigPeer::SetReceivedDisableConnectionMigration(
@@ -8831,7 +8842,7 @@
   base::RunLoop().RunUntilIdle();
   // Migration fails, and session is closed and deleted.
   EXPECT_FALSE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
 }
@@ -8944,7 +8955,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -8963,9 +8974,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Send GET request on stream.
@@ -9047,7 +9058,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -9058,8 +9069,8 @@
       ->NotifyNetworkDisconnected(kDefaultNetworkForTests);
   EXPECT_EQ(ERR_NETWORK_CHANGED, callback_.WaitForResult());
 
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
-  EXPECT_FALSE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
+  EXPECT_FALSE(HasActiveJob(scheme_host_port_, privacy_mode_));
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
 }
@@ -9090,7 +9101,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -9109,9 +9120,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   quic::QuicConnectionId cid_on_new_path =
       quic::test::TestConnectionId(12345678);
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
@@ -9190,7 +9201,7 @@
   // Verify the session is still alive and not marked as going away post
   // migration.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Verify that response headers on the migrated socket were delivered to the
@@ -9266,7 +9277,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -9285,9 +9296,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   quic::QuicConnectionId cid_on_new_path =
       quic::test::TestConnectionId(12345678);
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
@@ -9367,7 +9378,7 @@
   base::RunLoop().RunUntilIdle();
   // Verify session is still alive and not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Verify that response headers on the migrated socket were delivered to the
@@ -9448,7 +9459,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -9467,9 +9478,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   quic::QuicConnectionId cid_on_new_path =
       quic::test::TestConnectionId(12345678);
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
@@ -9482,7 +9493,7 @@
 
   // The connection should still be alive, not marked as going away.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
 
@@ -9552,7 +9563,7 @@
 
   // Ensure that the session is still alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Run the message loop migration for write error can finish.
@@ -9564,7 +9575,7 @@
 
   // Check that the session is still alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // There should be no posted tasks not executed, no way to migrate back to
   // default network.
@@ -9626,7 +9637,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -9645,9 +9656,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   quic::QuicConnectionId cid_on_new_path =
       quic::test::TestConnectionId(12345678);
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
@@ -9713,7 +9724,7 @@
   EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
 
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Verify that response headers on the migrated socket were delivered to the
@@ -9761,7 +9772,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -9780,9 +9791,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   quic::QuicConnectionId cid_on_new_path =
       quic::test::TestConnectionId(12345678);
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
@@ -9853,7 +9864,7 @@
   EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
 
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Send GET request on stream.
@@ -9873,7 +9884,7 @@
   socket_data.Resume();
   EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   stream.reset();
@@ -9912,7 +9923,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -9931,9 +9942,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   quic::QuicConnectionId cid_on_new_path =
       quic::test::TestConnectionId(12345678);
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
@@ -10004,7 +10015,7 @@
   socket_data.Resume();
   EXPECT_EQ(2u, task_runner->GetPendingTaskCount());
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Complete migration.
@@ -10012,7 +10023,7 @@
   EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
 
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Send GET request on stream.
@@ -10128,7 +10139,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -10147,8 +10158,8 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Now notify network is disconnected, cause the migration to complete
@@ -10159,7 +10170,7 @@
   // Complete migration.
   task_runner->RunUntilIdle();
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Send GET request on stream.
@@ -10197,7 +10208,7 @@
   // packet reader. Verify that the session is not affected.
   socket_data.Resume();
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   stream.reset();
@@ -10304,7 +10315,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -10323,8 +10334,8 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
 
   // Now notify network is disconnected, cause the migration to complete
@@ -10335,7 +10346,7 @@
   // Complete migration.
   task_runner->RunUntilIdle();
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Send GET request on stream.
@@ -10373,7 +10384,7 @@
   // packet reader. Verify that the session is not affected.
   socket_data.Resume();
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   stream.reset();
@@ -10453,7 +10464,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -10472,13 +10483,13 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Complete migration.
   task_runner->RunUntilIdle();
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Send GET request on stream.
@@ -10515,7 +10526,7 @@
   // Resume the old socket data, a read error will be delivered to the old
   // packet reader. Verify that the session is not affected.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   stream.reset();
@@ -10595,7 +10606,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -10614,13 +10625,13 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Complete migration.
   task_runner->RunUntilIdle();
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Send GET request on stream.
@@ -10656,7 +10667,7 @@
   // Resume the old socket data, a read error will be delivered to the old
   // packet reader. Verify that the session is not affected.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   stream.reset();
@@ -10736,7 +10747,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -10755,13 +10766,13 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Complete migration.
   task_runner->RunUntilIdle();
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Send GET request on stream.
@@ -10798,7 +10809,7 @@
   // Resume the old socket data, a read error will be delivered to the old
   // packet reader. Verify that the session is not affected.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   stream.reset();
@@ -10880,7 +10891,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -10899,13 +10910,13 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Complete migration.
   task_runner->RunUntilIdle();
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Send GET request on stream.
@@ -10941,7 +10952,7 @@
   // Resume the old socket data, a read error will be delivered to the old
   // packet reader. Verify that the session is not affected.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   stream.reset();
@@ -10979,7 +10990,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -10998,9 +11009,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   quic::QuicConnectionId cid_on_new_path =
       quic::test::TestConnectionId(12345678);
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
@@ -11046,7 +11057,7 @@
   // Verify session is not closed with read error.
   EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Complete migration.
@@ -11055,7 +11066,7 @@
   // default network.
   EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Verify that response headers on the migrated socket were delivered to the
@@ -11143,7 +11154,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -11162,9 +11173,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   quic::QuicConnectionId cid_on_new_path =
       quic::test::TestConnectionId(12345678);
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
@@ -11182,7 +11193,7 @@
   // In this particular code path, the network will not yet be marked
   // as going away and the session will still be alive.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
   EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
 
@@ -11261,7 +11272,7 @@
         ->NotifyNetworkDisconnected(kDefaultNetworkForTests);
   }
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // This is the callback for the response headers that returned
@@ -11277,7 +11288,7 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -11285,8 +11296,8 @@
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
   EXPECT_TRUE(stream2.get());
 
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
-  EXPECT_EQ(session, GetActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
+  EXPECT_EQ(session, GetActiveSession(scheme_host_port_));
 
   stream.reset();
   stream2.reset();
@@ -11461,7 +11472,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -11471,7 +11482,7 @@
   EXPECT_TRUE(stream.get());
 
   // Ensure that session is active.
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Trigger connection migration. Since there are no active streams,
   // the session will be closed.
@@ -11509,7 +11520,7 @@
       // Make new connection ID available.
       alternate_socket_data.Resume();
     }
-    EXPECT_TRUE(HasActiveSession(host_port_pair_));
+    EXPECT_TRUE(HasActiveSession(scheme_host_port_));
     // A task is posted to migrate back to the default network in 2^i seconds.
     EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
     EXPECT_EQ(base::TimeDelta::FromSeconds(UINT64_C(1) << i),
@@ -11659,7 +11670,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -11669,7 +11680,7 @@
   EXPECT_TRUE(stream.get());
 
   // Ensure that session is active.
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Trigger connection migration. Since there are no active streams,
   // the session will be closed.
@@ -11707,7 +11718,7 @@
       // Make new connection ID available.
       alternate_socket_data.Resume();
     }
-    EXPECT_TRUE(HasActiveSession(host_port_pair_));
+    EXPECT_TRUE(HasActiveSession(scheme_host_port_));
     // A task is posted to migrate back to the default network in 2^i seconds.
     EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
     EXPECT_EQ(base::TimeDelta::FromSeconds(UINT64_C(1) << i),
@@ -11749,7 +11760,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -11768,9 +11779,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   quic::QuicConnectionId cid_on_new_path =
       quic::test::TestConnectionId(12345678);
   MaybeMakeNewConnectionIdAvailableToSession(cid_on_new_path, session);
@@ -11840,7 +11851,7 @@
 
   // The session should be alive and active.
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_EQ(1u, session->GetNumActiveStreams());
 
   // Run the message loop so that data queued in the new socket is read by the
@@ -11878,7 +11889,7 @@
 
 TEST_P(QuicStreamFactoryTest, ServerMigrationIPv6ToIPv6) {
   // Add a resolver rule to make initial connection to an IPv6 address.
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             "fe80::aebc:32ff:febb:1e33", "");
   // Add alternate IPv6 server address to config.
   IPEndPoint alt_address = IPEndPoint(
@@ -11901,7 +11912,7 @@
   Initialize();
 
   // Add a resolver rule to make initial connection to an IPv6 address.
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             "fe80::aebc:32ff:febb:1e33", "");
   // Add alternate IPv4 server address to config.
   IPEndPoint alt_address = IPEndPoint(IPAddress(1, 2, 3, 4), 123);
@@ -11937,7 +11948,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -11956,9 +11967,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   IPEndPoint actual_address;
   session->GetDefaultSocket()->GetPeerAddress(&actual_address);
@@ -11983,7 +11994,7 @@
   Initialize();
 
   // Add a resolver rule to make initial connection to an IPv4 address.
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(), "1.2.3.4",
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(), "1.2.3.4",
                                             "");
   // Add alternate IPv6 server address to config.
   IPEndPoint alt_address = IPEndPoint(
@@ -12020,7 +12031,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -12039,9 +12050,9 @@
                                          net_log_, CompletionOnceCallback()));
 
   // Ensure that session is alive and active.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   IPEndPoint actual_address;
   session->GetDefaultSocket()->GetPeerAddress(&actual_address);
@@ -12081,7 +12092,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -12090,14 +12101,14 @@
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream);
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
 
   // Change the CA cert and verify that stream saw the event.
   factory_->OnCertDBChanged();
 
   EXPECT_TRUE(factory_->is_quic_known_to_work_on_current_network());
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
 
   // Now attempting to request a stream to the same origin should create
   // a new session.
@@ -12105,7 +12116,7 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -12114,8 +12125,8 @@
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
   EXPECT_TRUE(stream2);
-  QuicChromiumClientSession* session2 = GetActiveSession(host_port_pair_);
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  QuicChromiumClientSession* session2 = GetActiveSession(scheme_host_port_);
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
   EXPECT_NE(session, session2);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session2));
@@ -12142,14 +12153,14 @@
     r1_host_name.append(cannoncial_suffixes[i]);
     r2_host_name.append(cannoncial_suffixes[i]);
 
-    HostPortPair host_port_pair1(r1_host_name, 80);
+    url::SchemeHostPort scheme_host_port1(url::kHttpsScheme, r1_host_name, 80);
     // Need to hold onto this through the test, to keep the
     // QuicCryptoClientConfig alive.
     std::unique_ptr<QuicCryptoClientConfigHandle> crypto_config_handle =
         QuicStreamFactoryPeer::GetCryptoConfig(factory_.get(),
                                                NetworkIsolationKey());
-    quic::QuicServerId server_id1(host_port_pair1.host(),
-                                  host_port_pair1.port(), privacy_mode_);
+    quic::QuicServerId server_id1(scheme_host_port1.host(),
+                                  scheme_host_port1.port(), privacy_mode_);
     quic::QuicCryptoClientConfig::CachedState* cached1 =
         crypto_config_handle->GetConfig()->LookupOrCreate(server_id1);
     EXPECT_FALSE(cached1->proof_valid());
@@ -12160,9 +12171,9 @@
     cached1->set_source_address_token(r1_host_name);
     cached1->SetProofValid();
 
-    HostPortPair host_port_pair2(r2_host_name, 80);
-    quic::QuicServerId server_id2(host_port_pair2.host(),
-                                  host_port_pair2.port(), privacy_mode_);
+    url::SchemeHostPort scheme_host_port2(url::kHttpsScheme, r2_host_name, 80);
+    quic::QuicServerId server_id2(scheme_host_port2.host(),
+                                  scheme_host_port2.port(), privacy_mode_);
     quic::QuicCryptoClientConfig::CachedState* cached2 =
         crypto_config_handle->GetConfig()->LookupOrCreate(server_id2);
     EXPECT_EQ(cached1->source_address_token(), cached2->source_address_token());
@@ -12182,14 +12193,14 @@
     r3_host_name.append(cannoncial_suffixes[i]);
     r4_host_name.append(cannoncial_suffixes[i]);
 
-    HostPortPair host_port_pair1(r3_host_name, 80);
+    url::SchemeHostPort scheme_host_port1(url::kHttpsScheme, r3_host_name, 80);
     // Need to hold onto this through the test, to keep the
     // QuicCryptoClientConfig alive.
     std::unique_ptr<QuicCryptoClientConfigHandle> crypto_config_handle =
         QuicStreamFactoryPeer::GetCryptoConfig(factory_.get(),
                                                NetworkIsolationKey());
-    quic::QuicServerId server_id1(host_port_pair1.host(),
-                                  host_port_pair1.port(), privacy_mode_);
+    quic::QuicServerId server_id1(scheme_host_port1.host(),
+                                  scheme_host_port1.port(), privacy_mode_);
     quic::QuicCryptoClientConfig::CachedState* cached1 =
         crypto_config_handle->GetConfig()->LookupOrCreate(server_id1);
     EXPECT_FALSE(cached1->proof_valid());
@@ -12200,9 +12211,9 @@
     cached1->set_source_address_token(r3_host_name);
     cached1->SetProofInvalid();
 
-    HostPortPair host_port_pair2(r4_host_name, 80);
-    quic::QuicServerId server_id2(host_port_pair2.host(),
-                                  host_port_pair2.port(), privacy_mode_);
+    url::SchemeHostPort scheme_host_port2(url::kHttpsScheme, r4_host_name, 80);
+    quic::QuicServerId server_id2(scheme_host_port2.host(),
+                                  scheme_host_port2.port(), privacy_mode_);
     quic::QuicCryptoClientConfig::CachedState* cached2 =
         crypto_config_handle->GetConfig()->LookupOrCreate(server_id2);
     EXPECT_NE(cached1->source_address_token(), cached2->source_address_token());
@@ -12229,13 +12240,13 @@
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::ZERO_RTT);
   host_resolver_->set_synchronous_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             "192.168.0.1", "");
 
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -12273,12 +12284,13 @@
     socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data2.AddSocketDataToFactory(socket_factory_.get());
 
-  HostPortPair server2(kServer2HostName, kDefaultServerPort);
+  url::SchemeHostPort server2(url::kHttpsScheme, kServer2HostName,
+                              kDefaultServerPort);
 
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::CONFIRM_HANDSHAKE);
   host_resolver_->set_synchronous_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             "192.168.0.1", "");
   host_resolver_->rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
 
@@ -12289,13 +12301,13 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_EQ(quic::QuicTime::Delta::FromSeconds(quic::kPingTimeoutSecs),
             session->connection()->ping_timeout());
 
@@ -12632,7 +12644,7 @@
     QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), runner_.get());
 
     const AlternativeService alternative_service1(
-        kProtoQUIC, host_port_pair_.host(), host_port_pair_.port());
+        kProtoQUIC, scheme_host_port_.host(), scheme_host_port_.port());
     AlternativeServiceInfoVector alternative_service_info_vector;
     base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
     alternative_service_info_vector.push_back(
@@ -12697,9 +12709,11 @@
 
     QuicStreamRequest request(factory_.get());
     int rv = request.Request(
-        HostPortPair(kDefaultServerHostName, kDefaultServerPort), version_,
-        privacy_mode_, DEFAULT_PRIORITY, SocketTag(), network_isolation_keys[i],
-        SecureDnsPolicy::kAllow, true /* use_dns_aliases */,
+        url::SchemeHostPort(url::kHttpsScheme, kDefaultServerHostName,
+                            kDefaultServerPort),
+        version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+        network_isolation_keys[i], SecureDnsPolicy::kAllow,
+        true /* use_dns_aliases */,
         /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
         failed_on_default_network_callback_, callback_.callback());
     EXPECT_THAT(callback_.GetResult(rv), IsOk());
@@ -12747,7 +12761,7 @@
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::ZERO_RTT);
   host_resolver_->set_synchronous_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             "192.168.0.1", "");
 
   // Set up the TaskObserver to verify QuicChromiumPacketReader::StartReading
@@ -12759,7 +12773,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -12800,7 +12814,7 @@
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::ZERO_RTT);
   host_resolver_->set_synchronous_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             "192.168.0.1", "");
 
   // Set up the TaskObserver to verify QuicChromiumPacketReader::StartReading
@@ -12812,7 +12826,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -12848,7 +12862,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -12862,7 +12876,7 @@
 
   string url = "https://www.example.org/";
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
 
   quic::QuicClientPromisedInfo promised(
       session, GetNthServerInitiatedUnidirectionalStreamId(0), kDefaultUrl);
@@ -12871,7 +12885,7 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -12903,7 +12917,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -12916,7 +12930,7 @@
   EXPECT_EQ(0, QuicStreamFactoryPeer::GetNumPushStreamsCreated(factory_.get()));
 
   string url = "https://www.example.org/";
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
 
   quic::QuicClientPromisedInfo promised(
       session, GetNthServerInitiatedUnidirectionalStreamId(0), kDefaultUrl);
@@ -12931,7 +12945,7 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_, PRIVACY_MODE_ENABLED,
+                scheme_host_port_, version_, PRIVACY_MODE_ENABLED,
                 DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
                 SecureDnsPolicy::kAllow, true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -12984,7 +12998,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -12997,7 +13011,7 @@
   EXPECT_EQ(0, QuicStreamFactoryPeer::GetNumPushStreamsCreated(factory_.get()));
 
   string url = "https://www.example.org/";
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
 
   quic::QuicClientPromisedInfo promised(
       session, GetNthServerInitiatedUnidirectionalStreamId(0), kDefaultUrl);
@@ -13013,7 +13027,7 @@
   EXPECT_EQ(
       ERR_IO_PENDING,
       request2.Request(
-          host_port_pair_, version_, PRIVACY_MODE_DISABLED, DEFAULT_PRIORITY,
+          scheme_host_port_, version_, PRIVACY_MODE_DISABLED, DEFAULT_PRIORITY,
           SocketTag(), NetworkIsolationKey::CreateTransient(),
           SecureDnsPolicy::kAllow, true /* use_dns_aliases */,
           /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -13037,8 +13051,9 @@
 TEST_P(QuicStreamFactoryTest, PoolByOrigin) {
   Initialize();
 
-  HostPortPair destination1("first.example.com", 443);
-  HostPortPair destination2("second.example.com", 443);
+  url::SchemeHostPort destination1(url::kHttpsScheme, "first.example.com", 443);
+  url::SchemeHostPort destination2(url::kHttpsScheme, "second.example.com",
+                                   443);
 
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
@@ -13060,7 +13075,7 @@
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
   std::unique_ptr<HttpStream> stream1 = CreateStream(&request1);
   EXPECT_TRUE(stream1.get());
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Second request returns synchronously because it pools to existing session.
   TestCompletionCallback callback2;
@@ -13080,9 +13095,10 @@
   QuicChromiumClientSession::Handle* session2 =
       QuicHttpStreamPeer::GetSessionHandle(stream2.get());
   EXPECT_TRUE(session1->SharesSameSession(*session2));
-  EXPECT_EQ(quic::QuicServerId(host_port_pair_.host(), host_port_pair_.port(),
-                               privacy_mode_ == PRIVACY_MODE_ENABLED),
-            session1->server_id());
+  EXPECT_EQ(
+      quic::QuicServerId(scheme_host_port_.host(), scheme_host_port_.port(),
+                         privacy_mode_ == PRIVACY_MODE_ENABLED),
+      session1->server_id());
 
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
@@ -13154,17 +13170,17 @@
         destination_type_(GetParam().destination_type),
         hanging_read_(SYNCHRONOUS, ERR_IO_PENDING, 0) {}
 
-  HostPortPair GetDestination() {
+  url::SchemeHostPort GetDestination() {
     switch (destination_type_) {
       case SAME_AS_FIRST:
         return origin1_;
       case SAME_AS_SECOND:
         return origin2_;
       case DIFFERENT:
-        return HostPortPair(kDifferentHostname, 443);
+        return url::SchemeHostPort(url::kHttpsScheme, kDifferentHostname, 443);
       default:
         NOTREACHED();
-        return HostPortPair();
+        return url::SchemeHostPort();
     }
   }
 
@@ -13187,8 +13203,8 @@
   }
 
   DestinationType destination_type_;
-  HostPortPair origin1_;
-  HostPortPair origin2_;
+  url::SchemeHostPort origin1_;
+  url::SchemeHostPort origin2_;
   MockRead hanging_read_;
   std::vector<std::unique_ptr<SequencedSocketData>>
       sequenced_socket_data_vector_;
@@ -13208,13 +13224,13 @@
   Initialize();
 
   GURL url("https://mail.example.com/");
-  origin1_ = HostPortPair::FromURL(url);
+  origin1_ = url::SchemeHostPort(url);
 
   // Not used for requests, but this provides a test case where the certificate
   // is valid for the hostname of the alternative service.
-  origin2_ = HostPortPair("mail.example.org", 433);
+  origin2_ = url::SchemeHostPort(url::kHttpsScheme, "mail.example.org", 433);
 
-  HostPortPair destination = GetDestination();
+  url::SchemeHostPort destination = GetDestination();
 
   scoped_refptr<X509Certificate> cert(
       ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
@@ -13248,10 +13264,10 @@
 
   GURL url1("https://www.example.org/");
   GURL url2("https://mail.example.org/");
-  origin1_ = HostPortPair::FromURL(url1);
-  origin2_ = HostPortPair::FromURL(url2);
+  origin1_ = url::SchemeHostPort(url1);
+  origin2_ = url::SchemeHostPort(url2);
 
-  HostPortPair destination = GetDestination();
+  url::SchemeHostPort destination = GetDestination();
 
   scoped_refptr<X509Certificate> cert(
       ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
@@ -13317,10 +13333,10 @@
 
   GURL url1("https://www.example.org/");
   GURL url2("https://mail.example.org/");
-  origin1_ = HostPortPair::FromURL(url1);
-  origin2_ = HostPortPair::FromURL(url2);
+  origin1_ = url::SchemeHostPort(url1);
+  origin2_ = url::SchemeHostPort(url2);
 
-  HostPortPair destination = GetDestination();
+  url::SchemeHostPort destination = GetDestination();
 
   scoped_refptr<X509Certificate> cert(
       ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
@@ -13402,10 +13418,10 @@
 
   GURL url1("https://www.example.org/");
   GURL url2("https://mail.example.org/");
-  origin1_ = HostPortPair::FromURL(url1);
-  origin2_ = HostPortPair::FromURL(url2);
+  origin1_ = url::SchemeHostPort(url1);
+  origin2_ = url::SchemeHostPort(url2);
 
-  HostPortPair destination = GetDestination();
+  url::SchemeHostPort destination = GetDestination();
 
   scoped_refptr<X509Certificate> cert(
       ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
@@ -13481,10 +13497,10 @@
 
   GURL url1("https://news.example.org/");
   GURL url2("https://mail.example.com/");
-  origin1_ = HostPortPair::FromURL(url1);
-  origin2_ = HostPortPair::FromURL(url2);
+  origin1_ = url::SchemeHostPort(url1);
+  origin2_ = url::SchemeHostPort(url2);
 
-  HostPortPair destination = GetDestination();
+  url::SchemeHostPort destination = GetDestination();
 
   scoped_refptr<X509Certificate> cert1(
       ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
@@ -13656,7 +13672,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, MAXIMUM_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, MAXIMUM_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -13686,7 +13702,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, MAXIMUM_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, MAXIMUM_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -13698,7 +13714,7 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
@@ -13738,7 +13754,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), kNetworkIsolationKey, SecureDnsPolicy::kDisable,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -13793,7 +13809,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -13848,7 +13864,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -13900,7 +13916,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_QUIC_PROTOCOL_ERROR,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -13939,7 +13955,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -13968,13 +13984,13 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   // Host resolution will fail synchronously.
-  host_resolver_->rules()->AddSimulatedFailure(host_port_pair_.host());
+  host_resolver_->rules()->AddSimulatedFailure(scheme_host_port_.host());
   host_resolver_->set_synchronous_mode(true);
 
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_NAME_NOT_RESOLVED,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -13996,12 +14012,12 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  host_resolver_->rules()->AddSimulatedFailure(host_port_pair_.host());
+  host_resolver_->rules()->AddSimulatedFailure(scheme_host_port_.host());
 
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -14034,11 +14050,11 @@
 
   // Set an address in resolver for synchronous return.
   host_resolver_->set_synchronous_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             kNonCachedIPAddress, "");
 
   // Set up a different address in stale resolver cache.
-  HostCache::Key key(host_port_pair_.host(), DnsQueryType::UNSPECIFIED, 0,
+  HostCache::Key key(scheme_host_port_.host(), DnsQueryType::UNSPECIFIED, 0,
                      HostResolverSource::ANY, NetworkIsolationKey());
   HostCache::Entry entry(OK,
                          AddressList::CreateFromIPAddress(kCachedIPAddress, 0),
@@ -14057,7 +14073,7 @@
 
   QuicStreamRequest request(factory_.get());
   EXPECT_THAT(request.Request(
-                  host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                  scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                   SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                   true /* use_dns_aliases */,
                   /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -14065,7 +14081,7 @@
               IsOk());
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_EQ(session->peer_address().host().ToString(), kNonCachedIPAddress);
 
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
@@ -14083,7 +14099,7 @@
 
   // Set an address in resolver for asynchronous return.
   host_resolver_->set_ondemand_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             kNonCachedIPAddress, "");
 
   MockQuicData quic_data(version_);
@@ -14095,7 +14111,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -14113,7 +14129,7 @@
 
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
 
   EXPECT_EQ(session->peer_address().host().ToString(), kNonCachedIPAddress);
 
@@ -14132,11 +14148,11 @@
 
   // Set an address in resolver for asynchronous return.
   host_resolver_->set_ondemand_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             kCachedIPAddress.ToString(), "");
 
   // Set up the same address in the stale resolver cache.
-  HostCache::Key key(host_port_pair_.host(), DnsQueryType::UNSPECIFIED, 0,
+  HostCache::Key key(scheme_host_port_.host(), DnsQueryType::UNSPECIFIED, 0,
                      HostResolverSource::ANY, NetworkIsolationKey());
   HostCache::Entry entry(OK,
                          AddressList::CreateFromIPAddress(kCachedIPAddress, 0),
@@ -14156,15 +14172,15 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
   // Check that the racing job is running.
-  EXPECT_TRUE(HasLiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_TRUE(HasLiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // Resolve dns and return.
   host_resolver_->ResolveAllPending();
@@ -14172,7 +14188,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
 
   EXPECT_EQ(session->peer_address().host().ToString(),
             kCachedIPAddress.ToString());
@@ -14196,11 +14212,11 @@
   factory_->set_is_quic_known_to_work_on_current_network(false);
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::ZERO_RTT);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             kCachedIPAddress.ToString(), "");
 
   // Set up the same address in the stale resolver cache.
-  HostCache::Key key(host_port_pair_.host(), DnsQueryType::UNSPECIFIED, 0,
+  HostCache::Key key(scheme_host_port_.host(), DnsQueryType::UNSPECIFIED, 0,
                      HostResolverSource::ANY, NetworkIsolationKey());
   HostCache::Entry entry(OK,
                          AddressList::CreateFromIPAddress(kCachedIPAddress, 0),
@@ -14221,7 +14237,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -14233,8 +14249,8 @@
   base::RunLoop().RunUntilIdle();
 
   // Check that the racing job is running.
-  EXPECT_TRUE(HasLiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_TRUE(HasLiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // Resolve dns and call back, make sure job finishes.
   host_resolver_->ResolveAllPending();
@@ -14243,7 +14259,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
 
   EXPECT_EQ(session->peer_address().host().ToString(),
             kCachedIPAddress.ToString());
@@ -14267,11 +14283,11 @@
   factory_->set_is_quic_known_to_work_on_current_network(false);
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::ZERO_RTT);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             kCachedIPAddress.ToString(), "");
 
   // Set up the same address in the stale resolver cache.
-  HostCache::Key key(host_port_pair_.host(), DnsQueryType::UNSPECIFIED, 0,
+  HostCache::Key key(scheme_host_port_.host(), DnsQueryType::UNSPECIFIED, 0,
                      HostResolverSource::ANY, NetworkIsolationKey());
   HostCache::Entry entry(OK,
                          AddressList::CreateFromIPAddress(kCachedIPAddress, 0),
@@ -14292,7 +14308,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -14311,7 +14327,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_EQ(session->peer_address().host().ToString(),
             kCachedIPAddress.ToString());
 
@@ -14331,11 +14347,11 @@
 
   // Set an address in resolver for asynchronous return.
   host_resolver_->set_ondemand_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             kNonCachedIPAddress, "");
 
   // Set up a different address in the stale resolver cache.
-  HostCache::Key key(host_port_pair_.host(), DnsQueryType::UNSPECIFIED, 0,
+  HostCache::Key key(scheme_host_port_.host(), DnsQueryType::UNSPECIFIED, 0,
                      HostResolverSource::ANY, NetworkIsolationKey());
   HostCache::Entry entry(OK,
                          AddressList::CreateFromIPAddress(kCachedIPAddress, 0),
@@ -14371,15 +14387,15 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
   // Check the stale connection is running.
-  EXPECT_TRUE(HasLiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_TRUE(HasLiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // Finish dns resolution and check the job has finished.
   host_resolver_->ResolveAllPending();
@@ -14388,7 +14404,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
 
   EXPECT_EQ(session->peer_address().host().ToString(), kNonCachedIPAddress);
 
@@ -14412,11 +14428,11 @@
   factory_->set_is_quic_known_to_work_on_current_network(false);
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::ZERO_RTT);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             kNonCachedIPAddress, "");
 
   // Set up a different address in the stale resolver cache.
-  HostCache::Key key(host_port_pair_.host(), DnsQueryType::UNSPECIFIED, 0,
+  HostCache::Key key(scheme_host_port_.host(), DnsQueryType::UNSPECIFIED, 0,
                      HostResolverSource::ANY, NetworkIsolationKey());
   HostCache::Entry entry(OK,
                          AddressList::CreateFromIPAddress(kCachedIPAddress, 0),
@@ -14454,7 +14470,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -14464,8 +14480,8 @@
   crypto_client_stream_factory_.last_stream()
       ->NotifySessionOneRttKeyAvailable();
   base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(HasLiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_TRUE(HasLiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // Finish host resolution and check the job is done.
   host_resolver_->ResolveAllPending();
@@ -14474,7 +14490,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_EQ(session->peer_address().host().ToString(), kNonCachedIPAddress);
 
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
@@ -14497,11 +14513,11 @@
   factory_->set_is_quic_known_to_work_on_current_network(false);
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::ZERO_RTT);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             kNonCachedIPAddress, "");
 
   // Set up a different address in the stale resolver cache.
-  HostCache::Key key(host_port_pair_.host(), DnsQueryType::UNSPECIFIED, 0,
+  HostCache::Key key(scheme_host_port_.host(), DnsQueryType::UNSPECIFIED, 0,
                      HostResolverSource::ANY, NetworkIsolationKey());
   HostCache::Entry entry(OK,
                          AddressList::CreateFromIPAddress(kCachedIPAddress, 0),
@@ -14535,7 +14551,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -14550,7 +14566,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_EQ(session->peer_address().host().ToString(), kNonCachedIPAddress);
 
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
@@ -14570,7 +14586,7 @@
 
   // Set synchronous failure in resolver.
   host_resolver_->set_synchronous_mode(true);
-  host_resolver_->rules()->AddSimulatedFailure(host_port_pair_.host());
+  host_resolver_->rules()->AddSimulatedFailure(scheme_host_port_.host());
 
   MockQuicData quic_data(version_);
   quic_data.AddSocketDataToFactory(socket_factory_.get());
@@ -14578,7 +14594,7 @@
 
   EXPECT_EQ(ERR_NAME_NOT_RESOLVED,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -14596,7 +14612,7 @@
 
   // Set asynchronous failure in resolver.
   host_resolver_->set_ondemand_mode(true);
-  host_resolver_->rules()->AddSimulatedFailure(host_port_pair_.host());
+  host_resolver_->rules()->AddSimulatedFailure(scheme_host_port_.host());
 
   MockQuicData quic_data(version_);
   quic_data.AddSocketDataToFactory(socket_factory_.get());
@@ -14604,7 +14620,7 @@
 
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -14626,10 +14642,10 @@
 
   // Set asynchronous failure in resolver.
   host_resolver_->set_ondemand_mode(true);
-  host_resolver_->rules()->AddSimulatedFailure(host_port_pair_.host());
+  host_resolver_->rules()->AddSimulatedFailure(scheme_host_port_.host());
 
   // Set up an address in the stale cache.
-  HostCache::Key key(host_port_pair_.host(), DnsQueryType::UNSPECIFIED, 0,
+  HostCache::Key key(scheme_host_port_.host(), DnsQueryType::UNSPECIFIED, 0,
                      HostResolverSource::ANY, NetworkIsolationKey());
   HostCache::Entry entry(OK,
                          AddressList::CreateFromIPAddress(kCachedIPAddress, 0),
@@ -14657,15 +14673,15 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
   // Check that the stale connection is running.
-  EXPECT_TRUE(HasLiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_TRUE(HasLiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // Finish host resolution.
   host_resolver_->ResolveAllPending();
@@ -14687,11 +14703,11 @@
 
   // Set an address in host resolver for asynchronous return.
   host_resolver_->set_ondemand_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             kCachedIPAddress.ToString(), "");
 
   // Set up the same address in the stale resolver cache.
-  HostCache::Key key(host_port_pair_.host(), DnsQueryType::UNSPECIFIED, 0,
+  HostCache::Key key(scheme_host_port_.host(), DnsQueryType::UNSPECIFIED, 0,
                      HostResolverSource::ANY, NetworkIsolationKey());
   HostCache::Entry entry(OK,
                          AddressList::CreateFromIPAddress(kCachedIPAddress, 0),
@@ -14714,13 +14730,13 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
-  EXPECT_FALSE(HasLiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_FALSE(HasLiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   host_resolver_->ResolveAllPending();
   EXPECT_THAT(callback_.WaitForResult(), IsError(ERR_ADDRESS_IN_USE));
@@ -14737,11 +14753,11 @@
 
   // Set an address in host resolver.
   host_resolver_->set_ondemand_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             kNonCachedIPAddress, "");
 
   // Set up a different address in stale resolver cache.
-  HostCache::Key key(host_port_pair_.host(), DnsQueryType::UNSPECIFIED, 0,
+  HostCache::Key key(scheme_host_port_.host(), DnsQueryType::UNSPECIFIED, 0,
                      HostResolverSource::ANY, NetworkIsolationKey());
   HostCache::Entry entry(OK,
                          AddressList::CreateFromIPAddress(kCachedIPAddress, 0),
@@ -14767,15 +14783,15 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
   // Check that the stale connection fails.
-  EXPECT_FALSE(HasLiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_FALSE(HasLiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // Finish host resolution and check the job finishes ok.
   host_resolver_->ResolveAllPending();
@@ -14784,7 +14800,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
 
   EXPECT_EQ(session->peer_address().host().ToString(), kNonCachedIPAddress);
 
@@ -14803,11 +14819,11 @@
 
   // Set an address in host resolver asynchronously.
   host_resolver_->set_ondemand_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             kNonCachedIPAddress, "");
 
   // Set up a different address in the stale cache.
-  HostCache::Key key(host_port_pair_.host(), DnsQueryType::UNSPECIFIED, 0,
+  HostCache::Key key(scheme_host_port_.host(), DnsQueryType::UNSPECIFIED, 0,
                      HostResolverSource::ANY, NetworkIsolationKey());
   HostCache::Entry entry(OK,
                          AddressList::CreateFromIPAddress(kCachedIPAddress, 0),
@@ -14831,15 +14847,15 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
   // Check the stale connection fails.
-  EXPECT_FALSE(HasLiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_FALSE(HasLiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // Check the resolved dns connection fails.
   host_resolver_->ResolveAllPending();
@@ -14857,11 +14873,11 @@
 
   // Add asynchronous failure in host resolver.
   host_resolver_->set_ondemand_mode(true);
-  host_resolver_->rules()->AddSimulatedFailure(host_port_pair_.host());
+  host_resolver_->rules()->AddSimulatedFailure(scheme_host_port_.host());
   factory_->set_is_quic_known_to_work_on_current_network(false);
 
   // Set up an address in stale resolver cache.
-  HostCache::Key key(host_port_pair_.host(), DnsQueryType::UNSPECIFIED, 0,
+  HostCache::Key key(scheme_host_port_.host(), DnsQueryType::UNSPECIFIED, 0,
                      HostResolverSource::ANY, NetworkIsolationKey());
   HostCache::Entry entry(OK,
                          AddressList::CreateFromIPAddress(kCachedIPAddress, 0),
@@ -14889,7 +14905,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -14916,10 +14932,10 @@
   // Add asynchronous failure to host resolver.
   host_resolver_->set_ondemand_mode(true);
   factory_->set_is_quic_known_to_work_on_current_network(false);
-  host_resolver_->rules()->AddSimulatedFailure(host_port_pair_.host());
+  host_resolver_->rules()->AddSimulatedFailure(scheme_host_port_.host());
 
   // Set up an address in stale resolver cache.
-  HostCache::Key key(host_port_pair_.host(), DnsQueryType::UNSPECIFIED, 0,
+  HostCache::Key key(scheme_host_port_.host(), DnsQueryType::UNSPECIFIED, 0,
                      HostResolverSource::ANY, NetworkIsolationKey());
   HostCache::Entry entry(OK,
                          AddressList::CreateFromIPAddress(kCachedIPAddress, 0),
@@ -14946,7 +14962,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -14971,7 +14987,7 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   host_resolver_->set_ondemand_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             kNonCachedIPAddress, "");
 
   MockQuicData quic_data(version_);
@@ -14983,7 +14999,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -15020,11 +15036,11 @@
 
   // Set an address in resolver for asynchronous return.
   host_resolver_->set_ondemand_mode(true);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             kNonCachedIPAddress, "");
 
   // Set up the same address in the stale resolver cache.
-  HostCache::Key key(host_port_pair_.host(), DnsQueryType::UNSPECIFIED, 0,
+  HostCache::Key key(scheme_host_port_.host(), DnsQueryType::UNSPECIFIED, 0,
                      HostResolverSource::ANY, NetworkIsolationKey());
   HostCache::Entry entry(OK,
                          AddressList::CreateFromIPAddress(kCachedIPAddress, 0),
@@ -15052,15 +15068,15 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
   // Check that the racing job is running.
-  EXPECT_TRUE(HasLiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_TRUE(HasLiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // By disconnecting the network, the stale session will be killed.
   scoped_mock_network_change_notifier_->mock_network_change_notifier()
@@ -15072,7 +15088,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
 
   EXPECT_EQ(session->peer_address().host().ToString(), kNonCachedIPAddress);
 
@@ -15097,11 +15113,11 @@
   factory_->set_is_quic_known_to_work_on_current_network(false);
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::ZERO_RTT);
-  host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
+  host_resolver_->rules()->AddIPLiteralRule(scheme_host_port_.host(),
                                             kNonCachedIPAddress, "");
 
   // Set up a different address in the stale resolvercache.
-  HostCache::Key key(host_port_pair_.host(), DnsQueryType::UNSPECIFIED, 0,
+  HostCache::Key key(scheme_host_port_.host(), DnsQueryType::UNSPECIFIED, 0,
                      HostResolverSource::ANY, NetworkIsolationKey());
   HostCache::Entry entry(OK,
                          AddressList::CreateFromIPAddress(kCachedIPAddress, 0),
@@ -15129,15 +15145,15 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
   // Check that the racing job is running.
-  EXPECT_TRUE(HasLiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_TRUE(HasLiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // By disconnecting the network, the stale session will be killed.
   scoped_mock_network_change_notifier_->mock_network_change_notifier()
@@ -15153,7 +15169,7 @@
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
   EXPECT_TRUE(stream.get());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_EQ(session->peer_address().host().ToString(), kNonCachedIPAddress);
 
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
@@ -15199,15 +15215,15 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
-  EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
+  EXPECT_TRUE(HasActiveJob(scheme_host_port_, privacy_mode_));
 
   // The pending task is scheduled for handshake timeout retransmission,
   // which is 2 * 400ms with crypto frames and 1.5 * 400ms otherwise.
@@ -15228,7 +15244,7 @@
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
 
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_EQ(400000u, session->config()->GetInitialRoundTripTimeUsToSend());
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
@@ -15268,7 +15284,7 @@
   // Request a stream with |tag1|.
   QuicStreamRequest request1(factory_.get());
   int rv = request1.Request(
-      host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY, tag1,
+      scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY, tag1,
       NetworkIsolationKey(), SecureDnsPolicy::kAllow,
       true /* use_dns_aliases */,
       /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -15285,7 +15301,7 @@
   // Request a stream with |tag1| and verify underlying session is reused.
   QuicStreamRequest request2(factory_.get());
   rv = request2.Request(
-      host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY, tag1,
+      scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY, tag1,
       NetworkIsolationKey(), SecureDnsPolicy::kAllow,
       true /* use_dns_aliases */,
       /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -15300,7 +15316,7 @@
   // Request a stream with |tag2| and verify a new session is created.
   QuicStreamRequest request3(factory_.get());
   rv = request3.Request(
-      host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY, tag2,
+      scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY, tag2,
       NetworkIsolationKey(), SecureDnsPolicy::kAllow,
       true /* use_dns_aliases */,
       /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -15338,7 +15354,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -15348,14 +15364,14 @@
   EXPECT_TRUE(stream.get());
 
   // Ensure that the session is alive and active before we read the error.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Resume the socket data to get the read error delivered.
   socket_data.Resume();
   // Ensure that the session is no longer active.
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
+  EXPECT_FALSE(HasActiveSession(scheme_host_port_));
 }
 
 TEST_P(QuicStreamFactoryTest, MessageTooBigReadErrorDoesNotCloseConnection) {
@@ -15375,7 +15391,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -15385,14 +15401,14 @@
   EXPECT_TRUE(stream.get());
 
   // Ensure that the session is alive and active before we read the error.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Resume the socket data to get the read error delivered.
   socket_data.Resume();
   // Ensure that the session is still active.
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 }
 
 TEST_P(QuicStreamFactoryTest, ZeroLengthReadDoesNotCloseConnection) {
@@ -15412,7 +15428,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -15422,21 +15438,21 @@
   EXPECT_TRUE(stream.get());
 
   // Ensure that the session is alive and active before we read the error.
-  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
   EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 
   // Resume the socket data to get the zero-length read delivered.
   socket_data.Resume();
   // Ensure that the session is still active.
-  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_TRUE(HasActiveSession(scheme_host_port_));
 }
 
 TEST_P(QuicStreamFactoryTest, DnsAliasesCanBeAccessedFromStream) {
   std::vector<std::string> dns_aliases(
-      {"alias1", "alias2", host_port_pair_.host()});
+      {"alias1", "alias2", scheme_host_port_.host()});
   host_resolver_->rules()->AddIPLiteralRuleWithDnsAliases(
-      host_port_pair_.host(), "192.168.0.1", std::move(dns_aliases));
+      scheme_host_port_.host(), "192.168.0.1", std::move(dns_aliases));
 
   Initialize();
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
@@ -15451,7 +15467,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -15466,14 +15482,15 @@
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
 
-  EXPECT_THAT(stream->GetDnsAliases(),
-              testing::ElementsAre("alias1", "alias2", host_port_pair_.host()));
+  EXPECT_THAT(
+      stream->GetDnsAliases(),
+      testing::ElementsAre("alias1", "alias2", scheme_host_port_.host()));
 }
 
 TEST_P(QuicStreamFactoryTest, NoAdditionalDnsAliases) {
   std::vector<std::string> dns_aliases;
   host_resolver_->rules()->AddIPLiteralRuleWithDnsAliases(
-      host_port_pair_.host(), "192.168.0.1", std::move(dns_aliases));
+      scheme_host_port_.host(), "192.168.0.1", std::move(dns_aliases));
 
   Initialize();
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
@@ -15488,7 +15505,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -15504,13 +15521,13 @@
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
 
   EXPECT_THAT(stream->GetDnsAliases(),
-              testing::ElementsAre(host_port_pair_.host()));
+              testing::ElementsAre(scheme_host_port_.host()));
 }
 
 TEST_P(QuicStreamFactoryTest, DoNotUseDnsAliases) {
   std::vector<std::string> dns_aliases({"alias1", "alias2"});
   host_resolver_->rules()->AddIPLiteralRuleWithDnsAliases(
-      host_port_pair_.host(), "192.168.0.1", std::move(dns_aliases));
+      scheme_host_port_.host(), "192.168.0.1", std::move(dns_aliases));
 
   Initialize();
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
@@ -15525,7 +15542,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 false /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -15546,7 +15563,7 @@
 TEST_P(QuicStreamFactoryTest, ConnectErrorInCreateWithDnsAliases) {
   std::vector<std::string> dns_aliases({"alias1", "alias2"});
   host_resolver_->rules()->AddIPLiteralRuleWithDnsAliases(
-      host_port_pair_.host(), "192.168.0.1", std::move(dns_aliases));
+      scheme_host_port_.host(), "192.168.0.1", std::move(dns_aliases));
 
   Initialize();
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
@@ -15559,7 +15576,7 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
                 SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow,
                 true /* use_dns_aliases */,
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
@@ -15705,8 +15722,8 @@
 
   const GURL kUrl1(kDefaultUrl);
   const GURL kUrl2(kServer2Url);
-  const HostPortPair kOrigin1 = HostPortPair::FromURL(kUrl1);
-  const HostPortPair kOrigin2 = HostPortPair::FromURL(kUrl2);
+  const url::SchemeHostPort kOrigin1 = url::SchemeHostPort(kUrl1);
+  const url::SchemeHostPort kOrigin2 = url::SchemeHostPort(kUrl2);
 
   host_resolver_->rules()->AddIPLiteralRuleWithDnsAliases(
       kOrigin1.host(), "192.168.0.1", std::move(dns_aliases1_));
diff --git a/net/quic/quic_test_packet_maker.cc b/net/quic/quic_test_packet_maker.cc
index 7d1c223..e01e9e569 100644
--- a/net/quic/quic_test_packet_maker.cc
+++ b/net/quic/quic_test_packet_maker.cc
@@ -1214,7 +1214,7 @@
 void QuicTestPacketMaker::RemoveSavedStreamFrames(
     quic::QuicStreamId stream_id) {
   for (auto& kv : saved_frames_) {
-    auto it = kv.second.begin();
+    auto* it = kv.second.begin();
     while (it != kv.second.end()) {
       if (it->type == quic::STREAM_FRAME &&
           it->stream_frame.stream_id == stream_id) {
diff --git a/net/third_party/quiche/BUILD.gn b/net/third_party/quiche/BUILD.gn
index eff11d7..f8c3c37 100644
--- a/net/third_party/quiche/BUILD.gn
+++ b/net/third_party/quiche/BUILD.gn
@@ -45,6 +45,7 @@
     "src/common/platform/api/quiche_thread_local.h",
     "src/common/platform/api/quiche_time_utils.h",
     "src/common/platform/default/quiche_platform_impl/quiche_prefetch_impl.h",
+    "src/common/print_elements.h",
     "src/common/quiche_circular_deque.h",
     "src/common/quiche_data_reader.cc",
     "src/common/quiche_data_reader.h",
@@ -1415,7 +1416,6 @@
     "src/quic/core/tls_chlo_extractor_test.cc",
     "src/quic/core/uber_quic_stream_id_manager_test.cc",
     "src/quic/core/uber_received_packet_manager_test.cc",
-    "src/quic/platform/api/quic_containers_test.cc",
     "src/quic/platform/api/quic_hostname_utils_test.cc",
     "src/quic/platform/api/quic_ip_address_test.cc",
     "src/quic/platform/api/quic_mem_slice_span_test.cc",
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 29ce77cf..f6beb834 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -25,6 +25,7 @@
 #include "net/base/upload_data_stream.h"
 #include "net/cookies/cookie_store.h"
 #include "net/cookies/cookie_util.h"
+#include "net/cookies/same_party_context.h"
 #include "net/dns/public/secure_dns_policy.h"
 #include "net/http/http_log_util.h"
 #include "net/http/http_util.h"
@@ -548,8 +549,6 @@
       net_log_(NetLogWithSource::Make(context->net_log(),
                                       NetLogSourceType::URL_REQUEST)),
       url_chain_(1, url),
-      same_party_cookie_context_type_(
-          CookieOptions::SamePartyCookieContextType::kCrossParty),
       force_ignore_site_for_cookies_(false),
       force_ignore_top_frame_party_for_cookies_(false),
       method_("GET"),
@@ -618,13 +617,13 @@
   DCHECK(!is_pending_);
   DCHECK(!job_);
 
-  set_same_party_cookie_context_type(
+  set_same_party_context(
       context()->cookie_store()
           ? cookie_util::ComputeSamePartyContext(
                 SchemefulSite(url()), isolation_info(),
                 context()->cookie_store()->cookie_access_delegate(),
                 force_ignore_top_frame_party_for_cookies())
-          : CookieOptions::SamePartyCookieContextType::kCrossParty);
+          : SamePartyContext());
   privacy_mode_ = DeterminePrivacyMode();
 
   net_log_.BeginEvent(NetLogEventType::URL_REQUEST_START_JOB, [&] {
@@ -1073,7 +1072,7 @@
   if (network_delegate()) {
     enable_privacy_mode = network_delegate()->ForcePrivacyMode(
         url(), site_for_cookies_, isolation_info_.top_frame_origin(),
-        same_party_cookie_context_type());
+        same_party_context().context_type());
   }
   return enable_privacy_mode ? PRIVACY_MODE_ENABLED : PRIVACY_MODE_DISABLED;
 }
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index f3b04556..5cba789 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -32,6 +32,7 @@
 #include "net/base/request_priority.h"
 #include "net/base/upload_progress.h"
 #include "net/cookies/canonical_cookie.h"
+#include "net/cookies/same_party_context.h"
 #include "net/cookies/site_for_cookies.h"
 #include "net/dns/public/secure_dns_policy.h"
 #include "net/filter/source_stream.h"
@@ -262,13 +263,11 @@
 
   // The party_context_ of this leg of the request. This gets updated on
   // redirects.
-  CookieOptions::SamePartyCookieContextType same_party_cookie_context_type()
-      const {
-    return same_party_cookie_context_type_;
+  const SamePartyContext& same_party_context() const {
+    return same_party_context_;
   }
-  void set_same_party_cookie_context_type(
-      CookieOptions::SamePartyCookieContextType context_type) {
-    same_party_cookie_context_type_ = context_type;
+  void set_same_party_context(const SamePartyContext& context) {
+    same_party_context_ = context;
   }
 
   // Indicate whether SameSite cookies should be attached even though the
@@ -903,7 +902,7 @@
 
   IsolationInfo isolation_info_;
 
-  CookieOptions::SamePartyCookieContextType same_party_cookie_context_type_;
+  SamePartyContext same_party_context_;
 
   bool force_ignore_site_for_cookies_;
   bool force_ignore_top_frame_party_for_cookies_;
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index c302ef8..3f7b5bb 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -30,6 +30,7 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "net/base/features.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/http_user_agent_settings.h"
@@ -48,6 +49,7 @@
 #include "net/cookies/cookie_constants.h"
 #include "net/cookies/cookie_store.h"
 #include "net/cookies/cookie_util.h"
+#include "net/cookies/same_party_context.h"
 #include "net/filter/brotli_source_stream.h"
 #include "net/filter/filter_source_stream.h"
 #include "net/filter/gzip_source_stream.h"
@@ -158,14 +160,14 @@
 
 net::CookieOptions CreateCookieOptions(
     net::CookieOptions::SameSiteCookieContext same_site_context,
-    net::CookieOptions::SamePartyCookieContextType same_party_context,
+    const net::SamePartyContext& same_party_context,
     const net::IsolationInfo& isolation_info,
     bool is_in_nontrivial_first_party_set) {
   net::CookieOptions options;
   options.set_return_excluded_cookies();
   options.set_include_httponly();
   options.set_same_site_cookie_context(same_site_context);
-  options.set_same_party_cookie_context_type(same_party_context);
+  options.set_same_party_context(same_party_context);
   if (isolation_info.party_context().has_value()) {
     // Count the top-frame site since it's not in the party_context.
     options.set_full_party_context_size(isolation_info.party_context()->size() +
@@ -588,7 +590,7 @@
     bool is_in_nontrivial_first_party_set =
         delegate && delegate->IsInNontrivialFirstPartySet(request_site);
     CookieOptions options = CreateCookieOptions(
-        same_site_context, request_->same_party_cookie_context_type(),
+        same_site_context, request_->same_party_context(),
         request_->isolation_info(), is_in_nontrivial_first_party_set);
 
     UMA_HISTOGRAM_ENUMERATION(
@@ -761,7 +763,7 @@
   bool is_in_nontrivial_first_party_set =
       delegate && delegate->IsInNontrivialFirstPartySet(request_site);
   CookieOptions options = CreateCookieOptions(
-      same_site_context, request_->same_party_cookie_context_type(),
+      same_site_context, request_->same_party_context(),
       request_->isolation_info(), is_in_nontrivial_first_party_set);
 
   UMA_HISTOGRAM_ENUMERATION(
diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc
index 72b6dfc..e9bd7cea 100644
--- a/net/url_request/url_request_test_util.cc
+++ b/net/url_request/url_request_test_util.cc
@@ -19,6 +19,7 @@
 #include "net/cert/cert_verifier.h"
 #include "net/cert/ct_policy_enforcer.h"
 #include "net/cert/do_nothing_ct_verifier.h"
+#include "net/cookies/same_party_context.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/http/http_network_session.h"
 #include "net/http/http_response_headers.h"
@@ -621,8 +622,7 @@
     const GURL& url,
     const SiteForCookies& site_for_cookies,
     const absl::optional<url::Origin>& top_frame_origin,
-    CookieOptions::SamePartyCookieContextType same_party_cookie_context_type)
-    const {
+    SamePartyContext::Type same_party_context_type) const {
   return false;
 }
 
@@ -685,13 +685,12 @@
     const GURL& url,
     const SiteForCookies& site_for_cookies,
     const absl::optional<url::Origin>& top_frame_origin,
-    CookieOptions::SamePartyCookieContextType same_party_cookie_context_type)
-    const {
+    SamePartyContext::Type same_party_context_type) const {
   if (force_privacy_mode_)
     return true;
 
   return TestNetworkDelegate::OnForcePrivacyMode(
-      url, site_for_cookies, top_frame_origin, same_party_cookie_context_type);
+      url, site_for_cookies, top_frame_origin, same_party_context_type);
 }
 
 bool FilteringTestNetworkDelegate::OnAnnotateAndMoveUserBlockedCookies(
diff --git a/net/url_request/url_request_test_util.h b/net/url_request/url_request_test_util.h
index 3de10444..f975d09 100644
--- a/net/url_request/url_request_test_util.h
+++ b/net/url_request/url_request_test_util.h
@@ -29,6 +29,7 @@
 #include "net/cert/cert_verifier.h"
 #include "net/cert/ct_policy_enforcer.h"
 #include "net/cookies/cookie_monster.h"
+#include "net/cookies/same_party_context.h"
 #include "net/disk_cache/disk_cache.h"
 #include "net/ftp/ftp_network_layer.h"
 #include "net/http/http_auth_handler_factory.h"
@@ -370,11 +371,11 @@
       net::CookieAccessResultList& maybe_included_cookies,
       net::CookieAccessResultList& excluded_cookies,
       bool allowed_from_caller) override;
-  bool OnForcePrivacyMode(const GURL& url,
-                          const SiteForCookies& site_for_cookies,
-                          const absl::optional<url::Origin>& top_frame_origin,
-                          CookieOptions::SamePartyCookieContextType
-                              same_party_cookie_context_type) const override;
+  bool OnForcePrivacyMode(
+      const GURL& url,
+      const SiteForCookies& site_for_cookies,
+      const absl::optional<url::Origin>& top_frame_origin,
+      SamePartyContext::Type same_party_context_type) const override;
   bool OnCanSetCookie(const URLRequest& request,
                       const net::CanonicalCookie& cookie,
                       CookieOptions* options,
@@ -457,11 +458,11 @@
       net::CookieAccessResultList& excluded_cookies,
       bool allowed_from_caller) override;
 
-  bool OnForcePrivacyMode(const GURL& url,
-                          const SiteForCookies& site_for_cookies,
-                          const absl::optional<url::Origin>& top_frame_origin,
-                          CookieOptions::SamePartyCookieContextType
-                              same_party_cookie_context_type) const override;
+  bool OnForcePrivacyMode(
+      const GURL& url,
+      const SiteForCookies& site_for_cookies,
+      const absl::optional<url::Origin>& top_frame_origin,
+      SamePartyContext::Type same_party_context_type) const override;
 
   void set_block_annotate_cookies() { block_annotate_cookies_ = true; }
 
diff --git a/pdf/BUILD.gn b/pdf/BUILD.gn
index 4586a688..fd0b12dab 100644
--- a/pdf/BUILD.gn
+++ b/pdf/BUILD.gn
@@ -343,6 +343,7 @@
     visibility = [
       ":*",
       "//chrome/renderer",
+      "//components/pdf/renderer",
     ]
 
     sources = [
diff --git a/ppapi/proxy/proxy_channel.cc b/ppapi/proxy/proxy_channel.cc
index a63b1f84..6881cd0 100644
--- a/ppapi/proxy/proxy_channel.cc
+++ b/ppapi/proxy/proxy_channel.cc
@@ -7,7 +7,7 @@
 #include "base/logging.h"
 #include "build/build_config.h"
 #include "ipc/ipc_platform_file.h"
-#include "ipc/ipc_test_sink.h"
+#include "ipc/ipc_sender.h"
 
 #if defined(OS_NACL)
 #include <unistd.h>
@@ -44,9 +44,9 @@
   return true;
 }
 
-void ProxyChannel::InitWithTestSink(IPC::TestSink* test_sink) {
+void ProxyChannel::InitWithTestSink(IPC::Sender* sender) {
   DCHECK(!test_sink_);
-  test_sink_ = test_sink;
+  test_sink_ = sender;
 #if !defined(OS_NACL)
   peer_pid_ = base::GetCurrentProcId();
 #endif
diff --git a/ppapi/proxy/proxy_channel.h b/ppapi/proxy/proxy_channel.h
index b662558..dd4f7ac 100644
--- a/ppapi/proxy/proxy_channel.h
+++ b/ppapi/proxy/proxy_channel.h
@@ -24,10 +24,6 @@
 class WaitableEvent;
 }
 
-namespace IPC {
-class TestSink;
-}
-
 namespace ppapi {
 namespace proxy {
 
@@ -77,7 +73,7 @@
   // messages sent via this channel to the given test sink. The test sink
   // must outlive this class. In this case, the peer PID will be the current
   // process ID.
-  void InitWithTestSink(IPC::TestSink* test_sink);
+  void InitWithTestSink(IPC::Sender* sender);
 
   // Shares a file handle (HANDLE / file descriptor) with the remote side. It
   // returns a handle that should be sent in exactly one IPC message. Upon
@@ -141,7 +137,7 @@
   // When we're unit testing, this will indicate the sink for the messages to
   // be deposited so they can be inspected by the test. When non-NULL, this
   // indicates that the channel should not be used.
-  IPC::TestSink* test_sink_;
+  IPC::Sender* test_sink_;
 
   // Will be null for some tests when there is a test_sink_, and if the
   // remote side has crashed.
diff --git a/remoting/client/chromoting_session.cc b/remoting/client/chromoting_session.cc
index 66a4588..402106a 100644
--- a/remoting/client/chromoting_session.cc
+++ b/remoting/client/chromoting_session.cc
@@ -539,6 +539,7 @@
       new protocol::TransportContext(
           std::make_unique<protocol::ChromiumPortAllocatorFactory>(),
           runtime_->url_loader_factory(),
+          /* oauth_token_getter= */ nullptr,
           protocol::NetworkSettings(
               protocol::NetworkSettings::NAT_TRAVERSAL_FULL),
           protocol::TransportRole::CLIENT);
diff --git a/remoting/host/it2me/it2me_host.cc b/remoting/host/it2me/it2me_host.cc
index 6918084d..36bcebb 100644
--- a/remoting/host/it2me/it2me_host.cc
+++ b/remoting/host/it2me/it2me_host.cc
@@ -19,6 +19,7 @@
 #include "net/url_request/url_request_context_getter.h"
 #include "remoting/base/auto_thread.h"
 #include "remoting/base/logging.h"
+#include "remoting/base/oauth_token_getter.h"
 #include "remoting/base/rsa_key_pair.h"
 #include "remoting/base/service_urls.h"
 #include "remoting/host/chromoting_host.h"
@@ -143,6 +144,7 @@
   auto connection_context = std::move(create_context).Run(host_context_.get());
   log_to_server_ = std::move(connection_context->log_to_server);
   signal_strategy_ = std::move(connection_context->signal_strategy);
+  oauth_token_getter_ = std::move(connection_context->oauth_token_getter);
   DCHECK(log_to_server_);
   DCHECK(signal_strategy_);
 
@@ -213,8 +215,8 @@
   scoped_refptr<protocol::TransportContext> transport_context =
       new protocol::TransportContext(
           std::make_unique<protocol::ChromiumPortAllocatorFactory>(),
-          host_context_->url_loader_factory(), network_settings,
-          protocol::TransportRole::SERVER);
+          host_context_->url_loader_factory(), oauth_token_getter_.get(),
+          network_settings, protocol::TransportRole::SERVER);
   if (!ice_config.is_null()) {
     transport_context->set_turn_ice_config(ice_config);
   }
diff --git a/remoting/host/it2me/it2me_host.h b/remoting/host/it2me/it2me_host.h
index 264698e..ef67f62 100644
--- a/remoting/host/it2me/it2me_host.h
+++ b/remoting/host/it2me/it2me_host.h
@@ -36,6 +36,7 @@
 class HostEventLogger;
 class HostStatusLogger;
 class LogToServer;
+class OAuthTokenGetter;
 class RegisterSupportHostRequest;
 class RsaKeyPair;
 
@@ -54,6 +55,7 @@
     std::unique_ptr<LogToServer> log_to_server;
     std::unique_ptr<RegisterSupportHostRequest> register_request;
     std::unique_ptr<SignalStrategy> signal_strategy;
+    std::unique_ptr<OAuthTokenGetter> oauth_token_getter;
 
     // Since the deferred context only provides an interface* for the signal
     // strategy, we use this boolean to indicate whether the host process should
@@ -98,9 +100,6 @@
   // Methods called by the script object, from the plugin thread.
 
   // Creates It2Me host structures and starts the host.
-  //
-  // XmppLogToServer cannot be created and used in different sequence, so pass
-  // in a factory callback instead.
   virtual void Connect(
       std::unique_ptr<ChromotingHostContext> context,
       std::unique_ptr<base::DictionaryValue> policies,
@@ -185,6 +184,7 @@
   std::unique_ptr<SignalStrategy> signal_strategy_;
   std::unique_ptr<FtlSignalingConnector> ftl_signaling_connector_;
   std::unique_ptr<LogToServer> log_to_server_;
+  std::unique_ptr<OAuthTokenGetter> oauth_token_getter_;
 
   It2MeHostState state_ = It2MeHostState::kDisconnected;
 
diff --git a/remoting/host/it2me/it2me_native_messaging_host.cc b/remoting/host/it2me/it2me_native_messaging_host.cc
index 3a789a8..d4e6178a 100644
--- a/remoting/host/it2me/it2me_native_messaging_host.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host.cc
@@ -316,6 +316,9 @@
                     std::make_unique<PassthroughOAuthTokenGetter>(username,
                                                                   access_token),
                     host_context->url_loader_factory());
+            connection_context->oauth_token_getter =
+                std::make_unique<PassthroughOAuthTokenGetter>(username,
+                                                              access_token);
             return connection_context;
           },
           username, access_token);
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index b76530d..97acdb0 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -1557,8 +1557,8 @@
   scoped_refptr<protocol::TransportContext> transport_context =
       new protocol::TransportContext(
           std::make_unique<protocol::ChromiumPortAllocatorFactory>(),
-          context_->url_loader_factory(), network_settings,
-          protocol::TransportRole::SERVER);
+          context_->url_loader_factory(), oauth_token_getter_.get(),
+          network_settings, protocol::TransportRole::SERVER);
   std::unique_ptr<protocol::SessionManager> session_manager(
       new protocol::JingleSessionManager(signal_strategy_.get()));
 
diff --git a/remoting/protocol/BUILD.gn b/remoting/protocol/BUILD.gn
index 64acc85..b0a3e47 100644
--- a/remoting/protocol/BUILD.gn
+++ b/remoting/protocol/BUILD.gn
@@ -234,6 +234,7 @@
     "//jingle:webrtc_glue",
     "//net",
     "//remoting/base",
+    "//remoting/base:authorization",
     "//remoting/codec:decoder",
     "//remoting/proto/remoting/v1:network_traversal_proto",
     "//remoting/signaling",
diff --git a/remoting/protocol/ice_transport_unittest.cc b/remoting/protocol/ice_transport_unittest.cc
index 9a7a41d..73f7c57 100644
--- a/remoting/protocol/ice_transport_unittest.cc
+++ b/remoting/protocol/ice_transport_unittest.cc
@@ -119,7 +119,8 @@
 
     host_transport_ = std::make_unique<IceTransport>(
         new TransportContext(std::make_unique<ChromiumPortAllocatorFactory>(),
-                             nullptr, network_settings_, TransportRole::SERVER),
+                             nullptr, nullptr, network_settings_,
+                             TransportRole::SERVER),
         &host_event_handler_);
     if (!host_authenticator_) {
       host_authenticator_ =
@@ -128,7 +129,8 @@
 
     client_transport_ = std::make_unique<IceTransport>(
         new TransportContext(std::make_unique<ChromiumPortAllocatorFactory>(),
-                             nullptr, network_settings_, TransportRole::CLIENT),
+                             nullptr, nullptr, network_settings_,
+                             TransportRole::CLIENT),
         &client_event_handler_);
     if (!client_authenticator_) {
       client_authenticator_ =
diff --git a/remoting/protocol/remoting_ice_config_request.cc b/remoting/protocol/remoting_ice_config_request.cc
index cf92b70..28e14d5 100644
--- a/remoting/protocol/remoting_ice_config_request.cc
+++ b/remoting/protocol/remoting_ice_config_request.cc
@@ -57,10 +57,15 @@
 }  // namespace
 
 RemotingIceConfigRequest::RemotingIceConfigRequest(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    OAuthTokenGetter* oauth_token_getter)
     : http_client_(ServiceUrls::GetInstance()->remoting_server_endpoint(),
-                   nullptr,
-                   url_loader_factory) {}
+                   oauth_token_getter,
+                   url_loader_factory) {
+  // |oauth_token_getter| is allowed to be null if the caller wants the request
+  // to be unauthenticated.
+  make_authenticated_requests_ = oauth_token_getter != nullptr;
+}
 
 RemotingIceConfigRequest::~RemotingIceConfigRequest() = default;
 
@@ -72,19 +77,15 @@
 
   auto request_config =
       std::make_unique<ProtobufHttpRequestConfig>(kTrafficAnnotation);
-  request_config->authenticated = false;
   request_config->path = kGetIceConfigPath;
   request_config->request_message =
       std::make_unique<apis::v1::GetIceConfigRequest>();
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  // Use the default Chrome API key for ChromeOS as the only host instance
-  // which runs there is used for the ChromeOS Enterprise Kiosk mode
-  // scenario.  If we decide to implement a remote access host for ChromeOS,
-  // then we will need a way for the caller to provide an API key.
-  request_config->api_key = google_apis::GetAPIKey();
-#else
-  request_config->api_key = google_apis::GetRemotingAPIKey();
-#endif
+  if (!make_authenticated_requests_) {
+    // TODO(joedow): Remove this after we no longer have any clients/hosts which
+    // call this API in an unauthenticated fashion.
+    request_config->authenticated = false;
+    request_config->api_key = google_apis::GetRemotingAPIKey();
+  }
   auto request =
       std::make_unique<ProtobufHttpRequest>(std::move(request_config));
   request->SetResponseCallback(base::BindOnce(
diff --git a/remoting/protocol/remoting_ice_config_request.h b/remoting/protocol/remoting_ice_config_request.h
index a2a2197..0305501 100644
--- a/remoting/protocol/remoting_ice_config_request.h
+++ b/remoting/protocol/remoting_ice_config_request.h
@@ -26,6 +26,7 @@
 }  // namespace apis
 
 class ProtobufHttpStatus;
+class OAuthTokenGetter;
 
 namespace protocol {
 
@@ -33,8 +34,9 @@
 // service.
 class RemotingIceConfigRequest final : public IceConfigRequest {
  public:
-  explicit RemotingIceConfigRequest(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+  RemotingIceConfigRequest(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      OAuthTokenGetter* oauth_token_getter);
   ~RemotingIceConfigRequest() override;
 
   // IceConfigRequest implementation.
@@ -46,6 +48,7 @@
   void OnResponse(const ProtobufHttpStatus& status,
                   std::unique_ptr<apis::v1::GetIceConfigResponse> response);
 
+  bool make_authenticated_requests_ = false;
   OnIceConfigCallback on_ice_config_callback_;
   ProtobufHttpClient http_client_;
 
diff --git a/remoting/protocol/remoting_ice_config_request_unittest.cc b/remoting/protocol/remoting_ice_config_request_unittest.cc
index fd017a36..1844acd 100644
--- a/remoting/protocol/remoting_ice_config_request_unittest.cc
+++ b/remoting/protocol/remoting_ice_config_request_unittest.cc
@@ -42,7 +42,8 @@
 
   base::test::TaskEnvironment task_environment_;
   ProtobufHttpTestResponder test_responder_;
-  RemotingIceConfigRequest request_{test_responder_.GetUrlLoaderFactory()};
+  RemotingIceConfigRequest request_{test_responder_.GetUrlLoaderFactory(),
+                                    nullptr};
 };
 
 TEST_F(RemotingIceConfigRequestTest, SuccessfulRequest) {
diff --git a/remoting/protocol/transport_context.cc b/remoting/protocol/transport_context.cc
index 023ded4..ec6d0d7 100644
--- a/remoting/protocol/transport_context.cc
+++ b/remoting/protocol/transport_context.cc
@@ -15,6 +15,7 @@
 #include "jingle/glue/thread_wrapper.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "remoting/base/logging.h"
+#include "remoting/base/oauth_token_getter.h"
 #include "remoting/protocol/chromium_port_allocator_factory.h"
 #include "remoting/protocol/port_allocator_factory.h"
 #include "remoting/protocol/remoting_ice_config_request.h"
@@ -62,6 +63,7 @@
   jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
   return new protocol::TransportContext(
       std::make_unique<protocol::ChromiumPortAllocatorFactory>(), nullptr,
+      nullptr,
       protocol::NetworkSettings(
           protocol::NetworkSettings::NAT_TRAVERSAL_OUTGOING),
       role);
@@ -70,10 +72,12 @@
 TransportContext::TransportContext(
     std::unique_ptr<PortAllocatorFactory> port_allocator_factory,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    OAuthTokenGetter* oauth_token_getter,
     const NetworkSettings& network_settings,
     TransportRole role)
     : port_allocator_factory_(std::move(port_allocator_factory)),
       url_loader_factory_(url_loader_factory),
+      oauth_token_getter_(oauth_token_getter),
       network_settings_(network_settings),
       role_(role) {}
 
@@ -118,8 +122,8 @@
 
   if (base::Time::Now() >
       (last_request_completion_time_ + kIceConfigRequestCooldown)) {
-    ice_config_request_ =
-        std::make_unique<RemotingIceConfigRequest>(url_loader_factory_);
+    ice_config_request_ = std::make_unique<RemotingIceConfigRequest>(
+        url_loader_factory_, oauth_token_getter_);
     ice_config_request_->Send(
         base::BindOnce(&TransportContext::OnIceConfig, base::Unretained(this)));
   } else {
diff --git a/remoting/protocol/transport_context.h b/remoting/protocol/transport_context.h
index 6085dd0..27e73c3 100644
--- a/remoting/protocol/transport_context.h
+++ b/remoting/protocol/transport_context.h
@@ -26,6 +26,8 @@
 
 namespace remoting {
 
+class OAuthTokenGetter;
+
 namespace protocol {
 
 class PortAllocatorFactory;
@@ -44,6 +46,7 @@
   TransportContext(
       std::unique_ptr<PortAllocatorFactory> port_allocator_factory,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      OAuthTokenGetter* oauth_token_getter,
       const NetworkSettings& network_settings,
       TransportRole role);
 
@@ -96,6 +99,7 @@
 
   std::unique_ptr<PortAllocatorFactory> port_allocator_factory_;
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  OAuthTokenGetter* oauth_token_getter_ = nullptr;
   NetworkSettings network_settings_;
   TransportRole role_;
 
diff --git a/remoting/test/ftl_signaling_playground.cc b/remoting/test/ftl_signaling_playground.cc
index e7a650c7..c99c9ce 100644
--- a/remoting/test/ftl_signaling_playground.cc
+++ b/remoting/test/ftl_signaling_playground.cc
@@ -252,8 +252,8 @@
       protocol::NetworkSettings::NAT_TRAVERSAL_FULL);
   auto transport_context = base::MakeRefCounted<protocol::TransportContext>(
       std::make_unique<protocol::ChromiumPortAllocatorFactory>(),
-      url_loader_factory_owner_->GetURLLoaderFactory(), network_settings,
-      transport_role_);
+      url_loader_factory_owner_->GetURLLoaderFactory(), nullptr,
+      network_settings, transport_role_);
   auto close_callback =
       base::BindOnce(&FtlSignalingPlayground::AsyncTearDownAndRunCallback,
                      base::Unretained(this));
diff --git a/remoting/test/protocol_perftest.cc b/remoting/test/protocol_perftest.cc
index f9ca7e4..8cf971e 100644
--- a/remoting/test/protocol_perftest.cc
+++ b/remoting/test/protocol_perftest.cc
@@ -294,7 +294,7 @@
         GetParam().out_of_order_rate);
     scoped_refptr<protocol::TransportContext> transport_context(
         new protocol::TransportContext(std::move(port_allocator_factory),
-                                       nullptr, network_settings,
+                                       nullptr, nullptr, network_settings,
                                        protocol::TransportRole::SERVER));
     std::unique_ptr<protocol::SessionManager> session_manager(
         new protocol::JingleSessionManager(host_signaling_.get()));
@@ -361,7 +361,7 @@
         GetParam().out_of_order_rate);
     scoped_refptr<protocol::TransportContext> transport_context(
         new protocol::TransportContext(std::move(port_allocator_factory),
-                                       nullptr, network_settings,
+                                       nullptr, nullptr, network_settings,
                                        protocol::TransportRole::CLIENT));
 
     protocol::ClientAuthenticationConfig client_auth_config;
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
index cc0e91b2..17c7e13 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -64,6 +64,14 @@
 #if defined(__mips__) && !defined(MAP_STACK)
 #define MAP_STACK 0x40000
 #endif
+
+// Temporary definitions for Arm's Memory Tagging Extension (MTE) and Branch
+// Target Identification (BTI).
+#if defined(ARCH_CPU_ARM64)
+#define PROT_MTE 0x20
+#define PROT_BTI 0x10
+#endif
+
 namespace {
 
 inline bool IsArchitectureX86_64() {
@@ -229,7 +237,15 @@
   // "denied" mask because of the negation operator.
   // Significantly, we don't permit weird undocumented flags such as
   // PROT_GROWSDOWN.
-  const uint64_t kAllowedMask = PROT_READ | PROT_WRITE | PROT_EXEC;
+#if defined(ARCH_CPU_ARM64)
+  // Allows PROT_MTE and PROT_BTI (as explained higher up) on only Arm
+  // platforms.
+  const uint64_t kArchSpecificFlags = PROT_MTE | PROT_BTI;
+#else
+  const uint64_t kArchSpecificFlags = 0;
+#endif
+  const uint64_t kAllowedMask =
+      PROT_READ | PROT_WRITE | PROT_EXEC | kArchSpecificFlags;
   const Arg<int> prot(2);
   return If((prot & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS());
 }
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
index ba4289f..7e4dd6e9 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
@@ -39,6 +39,7 @@
 
 // Restrict the prot argument in mprotect(2).
 // Only allow: PROT_READ | PROT_WRITE | PROT_EXEC.
+// PROT_BTI | PROT_MTE is additionally allowed on 64-bit Arm.
 SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictMprotectFlags();
 
 // Restrict fcntl(2) cmd argument to:
diff --git a/services/network/cookie_access_delegate_impl.cc b/services/network/cookie_access_delegate_impl.cc
index ac41fa8b..7b40fc3 100644
--- a/services/network/cookie_access_delegate_impl.cc
+++ b/services/network/cookie_access_delegate_impl.cc
@@ -6,6 +6,7 @@
 
 #include "net/cookies/cookie_constants.h"
 #include "net/cookies/cookie_util.h"
+#include "net/cookies/same_party_context.h"
 #include "services/network/first_party_sets/first_party_sets.h"
 #include "services/network/public/cpp/is_potentially_trustworthy.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -54,12 +55,13 @@
   return false;
 }
 
-bool CookieAccessDelegateImpl::IsContextSamePartyWithSite(
+net::SamePartyContext CookieAccessDelegateImpl::ComputeSamePartyContext(
     const net::SchemefulSite& site,
-    const absl::optional<net::SchemefulSite>& top_frame_site,
+    const net::SchemefulSite* top_frame_site,
     const std::set<net::SchemefulSite>& party_context) const {
-  return first_party_sets_ && first_party_sets_->IsContextSamePartyWithSite(
-                                  site, top_frame_site, party_context);
+  return first_party_sets_ ? first_party_sets_->ComputeContext(
+                                 site, top_frame_site, party_context)
+                           : net::SamePartyContext();
 }
 
 net::FirstPartySetsContextType
diff --git a/services/network/cookie_access_delegate_impl.h b/services/network/cookie_access_delegate_impl.h
index 2234656..0d94904 100644
--- a/services/network/cookie_access_delegate_impl.h
+++ b/services/network/cookie_access_delegate_impl.h
@@ -8,6 +8,7 @@
 #include "base/component_export.h"
 #include "net/cookies/cookie_access_delegate.h"
 #include "net/cookies/cookie_constants.h"
+#include "net/cookies/same_party_context.h"
 #include "services/network/cookie_settings.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -47,9 +48,9 @@
   bool ShouldIgnoreSameSiteRestrictions(
       const GURL& url,
       const net::SiteForCookies& site_for_cookies) const override;
-  bool IsContextSamePartyWithSite(
+  net::SamePartyContext ComputeSamePartyContext(
       const net::SchemefulSite& site,
-      const absl::optional<net::SchemefulSite>& top_frame_site,
+      const net::SchemefulSite* top_frame_site,
       const std::set<net::SchemefulSite>& party_context) const override;
   net::FirstPartySetsContextType ComputeFirstPartySetsContextType(
       const net::SchemefulSite& site,
diff --git a/services/network/cookie_manager_unittest.cc b/services/network/cookie_manager_unittest.cc
index 7d235963..428ea681 100644
--- a/services/network/cookie_manager_unittest.cc
+++ b/services/network/cookie_manager_unittest.cc
@@ -29,6 +29,7 @@
 #include "net/cookies/cookie_store_test_callbacks.h"
 #include "net/cookies/cookie_store_test_helpers.h"
 #include "net/cookies/cookie_util.h"
+#include "net/cookies/same_party_context.h"
 #include "net/cookies/test_cookie_access_delegate.h"
 #include "net/url_request/url_request_context.h"
 #include "services/network/cookie_access_delegate_impl.h"
@@ -319,8 +320,7 @@
     net::CookieOptions options;
     options.set_same_site_cookie_context(
         net::CookieOptions::SameSiteCookieContext::MakeInclusive());
-    options.set_same_party_cookie_context_type(
-        net::CookieOptions::SamePartyCookieContextType::kSameParty);
+    options.set_same_party_context(net::SamePartyContext::MakeInclusive());
     options.set_is_in_nontrivial_first_party_set(true);
     if (can_modify_httponly)
       options.set_include_httponly();
@@ -886,8 +886,8 @@
     net::CookieOptions options;
     options.set_return_excluded_cookies();
     options.set_is_in_nontrivial_first_party_set(true);
-    ASSERT_EQ(net::CookieOptions::SamePartyCookieContextType::kCrossParty,
-              options.same_party_cookie_context_type());
+    ASSERT_EQ(net::SamePartyContext::Type::kCrossParty,
+              options.same_party_context().context_type());
     ASSERT_EQ(
         net::CookieOptions::SameSiteCookieContext(
             net::CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE),
@@ -911,8 +911,8 @@
     // In a same-party, cross-site context, SameParty cookies should be
     // included, and non-SameParty cookies should be excluded based on SameSite
     // value.
-    options.set_same_party_cookie_context_type(
-        net::CookieOptions::SamePartyCookieContextType::kSameParty);
+    options.set_same_party_context(
+        net::SamePartyContext(net::SamePartyContext::Type::kSameParty));
     EXPECT_THAT(service_wrapper()->GetCookieList(cookie_url, options),
                 UnorderedElementsAre(
                     net::MatchesCookieWithName("SameParty-Unspecified"),
@@ -947,8 +947,8 @@
     options.set_return_excluded_cookies();
     // Default, but set for explicitness.
     options.set_is_in_nontrivial_first_party_set(false);
-    ASSERT_EQ(net::CookieOptions::SamePartyCookieContextType::kCrossParty,
-              options.same_party_cookie_context_type());
+    ASSERT_EQ(net::SamePartyContext::Type::kCrossParty,
+              options.same_party_context().context_type());
     ASSERT_EQ(
         net::CookieOptions::SameSiteCookieContext(
             net::CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE),
@@ -969,8 +969,8 @@
             net::MatchesCookieAccessWithName("nonSameParty-Lax")));
 
     // Same-party, cross-site.
-    options.set_same_party_cookie_context_type(
-        net::CookieOptions::SamePartyCookieContextType::kSameParty);
+    options.set_same_party_context(
+        net::SamePartyContext(net::SamePartyContext::Type::kSameParty));
     EXPECT_THAT(
         service_wrapper()->GetCookieList(cookie_url, options),
         UnorderedElementsAre(net::MatchesCookieWithName("SameParty-None"),
diff --git a/services/network/cookie_settings.cc b/services/network/cookie_settings.cc
index 9caf159..5bf6d6e 100644
--- a/services/network/cookie_settings.cc
+++ b/services/network/cookie_settings.cc
@@ -25,8 +25,7 @@
 namespace network {
 namespace {
 
-using SamePartyCookieContextType =
-    net::CookieOptions::SamePartyCookieContextType;
+using SamePartyCookieContextType = net::SamePartyContext::Type;
 
 bool IsExplicitSetting(const ContentSettingPatternSource& setting) {
   return !setting.primary_pattern.MatchesAllHosts() ||
diff --git a/services/network/cookie_settings.h b/services/network/cookie_settings.h
index 8741f01..9583731 100644
--- a/services/network/cookie_settings.h
+++ b/services/network/cookie_settings.h
@@ -11,6 +11,7 @@
 #include "components/content_settings/core/common/cookie_settings_base.h"
 #include "net/base/features.h"
 #include "net/cookies/canonical_cookie.h"
+#include "net/cookies/same_party_context.h"
 #include "services/network/public/cpp/session_cookie_delete_predicate.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -88,11 +89,11 @@
 
   // Returns true iff "privacy mode" should be enabled for the URL request in
   // question, according to the user's settings.
-  bool IsPrivacyModeEnabled(const GURL& url,
-                            const GURL& site_for_cookies,
-                            const absl::optional<url::Origin>& top_frame_origin,
-                            net::CookieOptions::SamePartyCookieContextType
-                                same_party_cookie_context_type) const;
+  bool IsPrivacyModeEnabled(
+      const GURL& url,
+      const GURL& site_for_cookies,
+      const absl::optional<url::Origin>& top_frame_origin,
+      net::SamePartyContext::Type same_party_context_type) const;
 
   // Returns true if the given cookie is accessible according to user
   // cookie-blocking settings. Assumes that the cookie is otherwise accessible
diff --git a/services/network/cookie_settings_unittest.cc b/services/network/cookie_settings_unittest.cc
index ad39e12a..12c3a1bb 100644
--- a/services/network/cookie_settings_unittest.cc
+++ b/services/network/cookie_settings_unittest.cc
@@ -523,18 +523,18 @@
   // Enabled for third-party requests.
   EXPECT_TRUE(settings.IsPrivacyModeEnabled(
       GURL(kURL), GURL(), url::Origin::Create(GURL(kOtherURL)),
-      net::CookieOptions::SamePartyCookieContextType::kCrossParty));
+      net::SamePartyContext::Type::kCrossParty));
 
   // Enabled for requests with a null site_for_cookies, even if the
   // top_frame_origin matches.
   EXPECT_TRUE(settings.IsPrivacyModeEnabled(
       GURL(kURL), GURL(), url::Origin::Create(GURL(kURL)),
-      net::CookieOptions::SamePartyCookieContextType::kCrossParty));
+      net::SamePartyContext::Type::kCrossParty));
 
   // Disabled for first-party requests, if no other rule applies.
   EXPECT_FALSE(settings.IsPrivacyModeEnabled(
       GURL(kURL), GURL(kURL), url::Origin::Create(GURL(kURL)),
-      net::CookieOptions::SamePartyCookieContextType::kSameParty));
+      net::SamePartyContext::Type::kSameParty));
 
   // Enabled if there's a site-specific rule that blocks access, regardless of
   // the kind of request.
@@ -543,15 +543,15 @@
   // Third-party requests:
   EXPECT_TRUE(settings.IsPrivacyModeEnabled(
       GURL(kURL), GURL(), url::Origin::Create(GURL(kOtherURL)),
-      net::CookieOptions::SamePartyCookieContextType::kCrossParty));
+      net::SamePartyContext::Type::kCrossParty));
   // Requests with a null site_for_cookies, but matching top_frame_origin.
   EXPECT_TRUE(settings.IsPrivacyModeEnabled(
       GURL(kURL), GURL(), url::Origin::Create(GURL(kURL)),
-      net::CookieOptions::SamePartyCookieContextType::kCrossParty));
+      net::SamePartyContext::Type::kCrossParty));
   // First-party requests.
   EXPECT_TRUE(settings.IsPrivacyModeEnabled(
       GURL(kURL), GURL(kURL), url::Origin::Create(GURL(kURL)),
-      net::CookieOptions::SamePartyCookieContextType::kSameParty));
+      net::SamePartyContext::Type::kSameParty));
 
   // No histogram samples should have been recorded.
   EXPECT_THAT(histogram_tester.GetAllSamples(
@@ -570,19 +570,19 @@
   // Enabled for cross-party requests.
   EXPECT_TRUE(settings.IsPrivacyModeEnabled(
       GURL(kFPSMemberURL), GURL(), url::Origin::Create(GURL(kFPSOwnerURL)),
-      net::CookieOptions::SamePartyCookieContextType::kCrossParty));
+      net::SamePartyContext::Type::kCrossParty));
 
   // Disabled for cross-site, same-party requests.
   EXPECT_FALSE(settings.IsPrivacyModeEnabled(
       GURL(kFPSMemberURL), GURL(), url::Origin::Create(GURL(kFPSOwnerURL)),
-      net::CookieOptions::SamePartyCookieContextType::kSameParty));
+      net::SamePartyContext::Type::kSameParty));
 
   // Enabled for same-party requests if blocked by a site-specific rule.
   settings.set_content_settings(
       {CreateSetting(kFPSMemberURL, "*", CONTENT_SETTING_BLOCK)});
   EXPECT_TRUE(settings.IsPrivacyModeEnabled(
       GURL(kFPSMemberURL), GURL(), url::Origin::Create(GURL(kFPSOwnerURL)),
-      net::CookieOptions::SamePartyCookieContextType::kSameParty));
+      net::SamePartyContext::Type::kSameParty));
 
   // No histogram samples should have been recorded.
   EXPECT_THAT(histogram_tester.GetAllSamples(
diff --git a/services/network/first_party_sets/first_party_sets.cc b/services/network/first_party_sets/first_party_sets.cc
index 334bd67..81e7003f 100644
--- a/services/network/first_party_sets/first_party_sets.cc
+++ b/services/network/first_party_sets/first_party_sets.cc
@@ -15,6 +15,7 @@
 #include "base/strings/string_split.h"
 #include "net/base/schemeful_site.h"
 #include "net/cookies/cookie_constants.h"
+#include "net/cookies/same_party_context.h"
 #include "services/network/first_party_sets/first_party_set_parser.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -55,6 +56,11 @@
       std::make_pair(std::move(owner), std::move(members)));
 }
 
+net::SamePartyContext::Type ContextTypeFromBool(bool is_same_party) {
+  return is_same_party ? net::SamePartyContext::Type::kSameParty
+                       : net::SamePartyContext::Type::kCrossParty;
+}
+
 }  // namespace
 
 FirstPartySets::FirstPartySets() = default;
@@ -77,18 +83,20 @@
 
 bool FirstPartySets::IsContextSamePartyWithSite(
     const net::SchemefulSite& site,
-    const absl::optional<net::SchemefulSite>& top_frame_site,
-    const std::set<net::SchemefulSite>& party_context) const {
-  const auto it = sets_.find(site);
-  if (it == sets_.end())
+    const net::SchemefulSite* top_frame_site,
+    const std::set<net::SchemefulSite>& party_context,
+    bool infer_singleton_sets) const {
+  const net::SchemefulSite* site_owner = FindOwner(site, infer_singleton_sets);
+  if (!site_owner)
     return false;
-  const net::SchemefulSite& site_owner = it->second;
+
   const auto is_owned_by_site_owner =
-      [this, &site_owner](const net::SchemefulSite& context_site) {
-        const auto context_owner = sets_.find(context_site);
-        return context_owner != sets_.end() &&
-               context_owner->second == site_owner;
-      };
+      [this, site_owner,
+       infer_singleton_sets](const net::SchemefulSite& context_site) -> bool {
+    const net::SchemefulSite* context_owner =
+        FindOwner(context_site, infer_singleton_sets);
+    return context_owner && *context_owner == *site_owner;
+  };
 
   if (top_frame_site && !is_owned_by_site_owner(*top_frame_site))
     return false;
@@ -96,29 +104,42 @@
   return base::ranges::all_of(party_context, is_owned_by_site_owner);
 }
 
+net::SamePartyContext FirstPartySets::ComputeContext(
+    const net::SchemefulSite& site,
+    const net::SchemefulSite* top_frame_site,
+    const std::set<net::SchemefulSite>& party_context) const {
+  net::SamePartyContext::Type context_type = ContextTypeFromBool(
+      IsContextSamePartyWithSite(site, top_frame_site, party_context,
+                                 false /* infer_singleton_sets */));
+  net::SamePartyContext::Type ancestors = ContextTypeFromBool(
+      IsContextSamePartyWithSite(site, top_frame_site, party_context,
+                                 true /* infer_singleton_sets */));
+  net::SamePartyContext::Type top_resource =
+      ContextTypeFromBool(IsContextSamePartyWithSite(
+          site, top_frame_site, {}, true /* infer_singleton_sets */));
+
+  return net::SamePartyContext(context_type, ancestors, top_resource);
+}
+
 net::FirstPartySetsContextType FirstPartySets::ComputeContextType(
     const net::SchemefulSite& site,
     const absl::optional<net::SchemefulSite>& top_frame_site,
     const std::set<net::SchemefulSite>& party_context) const {
-  const auto owner_or_site =
-      [this](const net::SchemefulSite& site) -> const net::SchemefulSite& {
-    const auto it = sets_.find(site);
-    return it == sets_.end() ? site : it->second;
-  };
-  const net::SchemefulSite& site_owner = owner_or_site(site);
+  constexpr bool infer_singleton_sets = true;
+  const net::SchemefulSite* site_owner = FindOwner(site, infer_singleton_sets);
   // Note: the `party_context` consists of the intermediate frames (for frame
   // requests) or intermediate frames and current frame for subresource
   // requests.
   const bool is_homogeneous = base::ranges::all_of(
       party_context, [&](const net::SchemefulSite& middle_site) {
-        return owner_or_site(middle_site) == site_owner;
+        return *FindOwner(middle_site, infer_singleton_sets) == *site_owner;
       });
   if (!top_frame_site.has_value()) {
     return is_homogeneous
                ? net::FirstPartySetsContextType::kTopFrameIgnoredHomogeneous
                : net::FirstPartySetsContextType::kTopFrameIgnoredMixed;
   }
-  if (owner_or_site(*top_frame_site) != site_owner)
+  if (*FindOwner(*top_frame_site, infer_singleton_sets) != *site_owner)
     return net::FirstPartySetsContextType::kTopResourceMismatch;
 
   return is_homogeneous
@@ -126,6 +147,15 @@
              : net::FirstPartySetsContextType::kTopResourceMatchMixed;
 }
 
+const net::SchemefulSite* FirstPartySets::FindOwner(
+    const net::SchemefulSite& site,
+    bool infer_singleton_sets) const {
+  const auto it = sets_.find(site);
+  if (it == sets_.end())
+    return infer_singleton_sets ? &site : nullptr;
+  return &it->second;
+}
+
 bool FirstPartySets::IsInNontrivialFirstPartySet(
     const net::SchemefulSite& site) const {
   return base::Contains(sets_, site);
diff --git a/services/network/first_party_sets/first_party_sets.h b/services/network/first_party_sets/first_party_sets.h
index 4e2ec20..81e0e10 100644
--- a/services/network/first_party_sets/first_party_sets.h
+++ b/services/network/first_party_sets/first_party_sets.h
@@ -13,6 +13,7 @@
 #include "base/containers/flat_set.h"
 #include "net/base/schemeful_site.h"
 #include "net/cookies/cookie_constants.h"
+#include "net/cookies/same_party_context.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace network {
@@ -42,14 +43,24 @@
       base::StringPiece raw_sets);
 
   // Returns whether the `site` is same-party with the `party_context`, and
-  // `top_frame_site` (if it is not nullopt). That is, is the `site`'s owner the
+  // `top_frame_site` (if it is not nullptr). That is, is the `site`'s owner the
   // same as the owners of every member of `party_context` and of
   // `top_frame_site`? Note: if `site` is not a member of a First-Party Set
   // (with more than one member), then this returns false. If `top_frame_site`
-  // is nullopt, then it is ignored.
+  // is nullptr, then it is ignored.
   bool IsContextSamePartyWithSite(
       const net::SchemefulSite& site,
-      const absl::optional<net::SchemefulSite>& top_frame_site,
+      const net::SchemefulSite* top_frame_site,
+      const std::set<net::SchemefulSite>& party_context,
+      bool infer_singleton_sets) const;
+
+  // Computes the SameParty context, indicating whether `site` is same-party
+  // with `top_frame_site` (if not nullptr) and `party_context`. The context
+  // includes the real context type, plus some additional "hypothetical" context
+  // types for metrics.
+  net::SamePartyContext ComputeContext(
+      const net::SchemefulSite& site,
+      const net::SchemefulSite* top_frame_site,
       const std::set<net::SchemefulSite>& party_context) const;
 
   // Computes the "type" of the context. I.e., categorizes contexts based on
@@ -77,6 +88,12 @@
   base::flat_map<net::SchemefulSite, std::set<net::SchemefulSite>> Sets() const;
 
  private:
+  // Returns a pointer to `site`'s owner (optionally inferring a singleton set
+  // if necessary), or `nullptr` if `site` has no owner. Must not return
+  // `nullptr` if `infer_singleton_sets` is true.
+  const net::SchemefulSite* FindOwner(const net::SchemefulSite& site,
+                                      bool infer_singleton_sets) const;
+
   // We must ensure there's no intersection between the manually-specified set
   // and the sets that came from Component Updater. (When reconciling the
   // manually-specified set and `sets_`, entries in the manually-specified set
diff --git a/services/network/first_party_sets/first_party_sets_unittest.cc b/services/network/first_party_sets/first_party_sets_unittest.cc
index f9e2812..b929315 100644
--- a/services/network/first_party_sets/first_party_sets_unittest.cc
+++ b/services/network/first_party_sets/first_party_sets_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/json/json_reader.h"
 #include "net/base/schemeful_site.h"
 #include "net/cookies/cookie_constants.h"
+#include "net/cookies/same_party_context.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -506,25 +507,27 @@
 };
 
 TEST_F(FirstPartySetsTest, IsContextSamePartyWithSite_EmptyContext) {
-  for (const absl::optional<net::SchemefulSite>& top_frame :
-       std::initializer_list<absl::optional<net::SchemefulSite>>{
-           net::SchemefulSite(GURL("https://example.test")), absl::nullopt}) {
+  net::SchemefulSite example_site(GURL("https://example.test"));
+  net::SchemefulSite nonmember(GURL("https://nonmember.test"));
+
+  for (const net::SchemefulSite* top_frame :
+       std::initializer_list<net::SchemefulSite*>{&example_site, nullptr}) {
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://nonmember.test")), top_frame, {}));
+        net::SchemefulSite(GURL("https://nonmember.test")), top_frame, {},
+        false /* infer_singleton_sets */));
 
     EXPECT_TRUE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://example.test")), top_frame, {}));
+        example_site, top_frame, {}, false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("http://example.test")), top_frame, {}));
+        net::SchemefulSite(GURL("http://example.test")), top_frame, {},
+        false /* infer_singleton_sets */));
   }
 
   EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-      net::SchemefulSite(GURL("https://example.test")),
-      net::SchemefulSite(GURL("https://nonmember.test")), {}));
+      example_site, &nonmember, {}, false /* infer_singleton_sets */));
   EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-      net::SchemefulSite(GURL("https://nonmember.test")),
-      net::SchemefulSite(GURL("https://example.test")), {}));
+      nonmember, &example_site, {}, false /* infer_singleton_sets */));
 }
 
 TEST_F(FirstPartySetsTest, IsContextSamePartyWithSite_ContextIsNonmember) {
@@ -532,27 +535,33 @@
       net::SchemefulSite(GURL("https://nonmember.test")),
   });
 
-  for (const absl::optional<net::SchemefulSite>& top_frame :
-       std::initializer_list<absl::optional<net::SchemefulSite>>{
-           net::SchemefulSite(GURL("https://example.test")), absl::nullopt}) {
+  net::SchemefulSite example_site(GURL("https://example.test"));
+
+  for (const net::SchemefulSite* top_frame :
+       std::initializer_list<net::SchemefulSite*>{&example_site, nullptr}) {
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://example.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://example.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("http://example.test")), top_frame, context));
+        net::SchemefulSite(GURL("http://example.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://member1.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://member1.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://foo.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://foo.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://member2.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://member2.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://nonmember.test")), top_frame,
-        context));
+        net::SchemefulSite(GURL("https://nonmember.test")), top_frame, context,
+        false /* infer_singleton_sets */));
   }
 }
 
@@ -560,27 +569,33 @@
   std::set<net::SchemefulSite> context(
       {net::SchemefulSite(GURL("https://example.test"))});
 
-  for (const absl::optional<net::SchemefulSite>& top_frame :
-       std::initializer_list<absl::optional<net::SchemefulSite>>{
-           net::SchemefulSite(GURL("https://example.test")), absl::nullopt}) {
+  net::SchemefulSite example_site(GURL("https://example.test"));
+
+  for (const net::SchemefulSite* top_frame :
+       std::initializer_list<net::SchemefulSite*>{&example_site, nullptr}) {
     EXPECT_TRUE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://example.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://example.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("http://example.test")), top_frame, context));
+        net::SchemefulSite(GURL("http://example.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_TRUE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://member1.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://member1.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://foo.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://foo.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://member2.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://member2.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://nonmember.test")), top_frame,
-        context));
+        net::SchemefulSite(GURL("https://nonmember.test")), top_frame, context,
+        false /* infer_singleton_sets */));
   }
 }
 
@@ -588,30 +603,37 @@
   std::set<net::SchemefulSite> context(
       {net::SchemefulSite(GURL("https://member1.test"))});
 
-  for (const absl::optional<net::SchemefulSite>& top_frame :
-       std::initializer_list<absl::optional<net::SchemefulSite>>{
-           net::SchemefulSite(GURL("https://example.test")), absl::nullopt}) {
+  net::SchemefulSite example_site(GURL("https://example.test"));
+
+  for (const net::SchemefulSite* top_frame :
+       std::initializer_list<net::SchemefulSite*>{&example_site, nullptr}) {
     EXPECT_TRUE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://example.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://example.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("http://example.test")), top_frame, context));
+        net::SchemefulSite(GURL("http://example.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_TRUE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://example.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://example.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_TRUE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://member1.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://member1.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://foo.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://foo.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://member2.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://member2.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://nonmember.test")), top_frame,
-        context));
+        net::SchemefulSite(GURL("https://nonmember.test")), top_frame, context,
+        false /* infer_singleton_sets */));
   }
 }
 
@@ -621,30 +643,37 @@
       net::SchemefulSite(GURL("https://member1.test")),
   });
 
-  for (const absl::optional<net::SchemefulSite>& top_frame :
-       std::initializer_list<absl::optional<net::SchemefulSite>>{
-           net::SchemefulSite(GURL("https://example.test")), absl::nullopt}) {
+  net::SchemefulSite example_site(GURL("https://example.test"));
+
+  for (const net::SchemefulSite* top_frame :
+       std::initializer_list<net::SchemefulSite*>{&example_site, nullptr}) {
     EXPECT_TRUE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://example.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://example.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("http://example.test")), top_frame, context));
+        net::SchemefulSite(GURL("http://example.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_TRUE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://member1.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://member1.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_TRUE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://member3.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://member3.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://foo.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://foo.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://member2.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://member2.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://nonmember.test")), top_frame,
-        context));
+        net::SchemefulSite(GURL("https://nonmember.test")), top_frame, context,
+        false /* infer_singleton_sets */));
   }
 }
 
@@ -655,27 +684,33 @@
       net::SchemefulSite(GURL("https://foo.test")),
   });
 
-  for (const absl::optional<net::SchemefulSite>& top_frame :
-       std::initializer_list<absl::optional<net::SchemefulSite>>{
-           net::SchemefulSite(GURL("https://example.test")), absl::nullopt}) {
+  net::SchemefulSite example_site(GURL("https://example.test"));
+
+  for (const net::SchemefulSite* top_frame :
+       std::initializer_list<net::SchemefulSite*>{&example_site, nullptr}) {
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://example.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://example.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("http://example.test")), top_frame, context));
+        net::SchemefulSite(GURL("http://example.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://member1.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://member1.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://foo.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://foo.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://member2.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://member2.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://nonmember.test")), top_frame,
-        context));
+        net::SchemefulSite(GURL("https://nonmember.test")), top_frame, context,
+        false /* infer_singleton_sets */));
   }
 }
 
@@ -687,27 +722,33 @@
       net::SchemefulSite(GURL("http://nonmember.test")),
   });
 
-  for (const absl::optional<net::SchemefulSite>& top_frame :
-       std::initializer_list<absl::optional<net::SchemefulSite>>{
-           net::SchemefulSite(GURL("https://example.test")), absl::nullopt}) {
+  net::SchemefulSite example_site(GURL("https://example.test"));
+
+  for (const net::SchemefulSite* top_frame :
+       std::initializer_list<net::SchemefulSite*>{&example_site, nullptr}) {
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://example.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://example.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("http://example.test")), top_frame, context));
+        net::SchemefulSite(GURL("http://example.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://member1.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://member1.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://foo.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://foo.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://member2.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://member2.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://nonmember.test")), top_frame,
-        context));
+        net::SchemefulSite(GURL("https://nonmember.test")), top_frame, context,
+        false /* infer_singleton_sets */));
   }
 }
 
@@ -718,30 +759,132 @@
       net::SchemefulSite(GURL("http://example.test")),
   });
 
-  for (const absl::optional<net::SchemefulSite>& top_frame :
-       std::initializer_list<absl::optional<net::SchemefulSite>>{
-           net::SchemefulSite(GURL("https://example.test")), absl::nullopt}) {
+  net::SchemefulSite example_site(GURL("https://example.test"));
+
+  for (const net::SchemefulSite* top_frame :
+       std::initializer_list<net::SchemefulSite*>{&example_site, nullptr}) {
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://example.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://example.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("http://example.test")), top_frame, context));
+        net::SchemefulSite(GURL("http://example.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://member1.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://member1.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://foo.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://foo.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://member2.test")), top_frame, context));
+        net::SchemefulSite(GURL("https://member2.test")), top_frame, context,
+        false /* infer_singleton_sets */));
 
     EXPECT_FALSE(sets().IsContextSamePartyWithSite(
-        net::SchemefulSite(GURL("https://nonmember.test")), top_frame,
-        context));
+        net::SchemefulSite(GURL("https://nonmember.test")), top_frame, context,
+        false /* infer_singleton_sets */));
   }
 }
 
+TEST_F(FirstPartySetsTest, IsContextSamePartyWithSite_InfersSingletonSets) {
+  std::set<net::SchemefulSite> context({
+      net::SchemefulSite(GURL("https://nonmember.test")),
+  });
+  net::SchemefulSite nonmember(GURL("https://nonmember.test"));
+  net::SchemefulSite nonmember2(GURL("https://nonmember2.test"));
+  net::SchemefulSite member(GURL("https://member1.test"));
+
+  EXPECT_TRUE(sets().IsContextSamePartyWithSite(nonmember, nullptr, {}, true));
+
+  EXPECT_TRUE(
+      sets().IsContextSamePartyWithSite(nonmember, &nonmember, {}, true));
+
+  EXPECT_TRUE(
+      sets().IsContextSamePartyWithSite(nonmember, nullptr, context, true));
+
+  EXPECT_TRUE(
+      sets().IsContextSamePartyWithSite(nonmember, &nonmember, context, true));
+
+  // Context mismatches.
+  EXPECT_FALSE(sets().IsContextSamePartyWithSite(nonmember, &nonmember,
+                                                 {nonmember2}, true));
+
+  // Context mismatches (but is a member of some set).
+  EXPECT_FALSE(
+      sets().IsContextSamePartyWithSite(nonmember, &nonmember, {member}, true));
+
+  // Top frame mismatches.
+  EXPECT_FALSE(
+      sets().IsContextSamePartyWithSite(nonmember, &member, {nonmember}, true));
+
+  // Request URL mismatches.
+  EXPECT_FALSE(sets().IsContextSamePartyWithSite(
+      net::SchemefulSite(GURL("https://nonmember1.test")), &nonmember2,
+      {nonmember2}, true));
+
+  // Request URL mismatches (but is a member of some set).
+  EXPECT_FALSE(
+      sets().IsContextSamePartyWithSite(member, &nonmember, {nonmember}, true));
+}
+
+TEST_F(FirstPartySetsTest, ComputeContext) {
+  using SamePartyContextType = net::SamePartyContext::Type;
+
+  net::SchemefulSite nonmember(GURL("https://nonmember.test"));
+  net::SchemefulSite nonmember1(GURL("https://nonmember1.test"));
+  net::SchemefulSite member(GURL("https://member1.test"));
+  net::SchemefulSite owner(GURL("https://example.test"));
+
+  // Works as usual for sites that are in First-Party sets.
+  EXPECT_THAT(sets().ComputeContext(member, &member, {member}),
+              net::SamePartyContext(SamePartyContextType::kSameParty));
+  EXPECT_THAT(sets().ComputeContext(owner, &member, {member}),
+              net::SamePartyContext(SamePartyContextType::kSameParty));
+  EXPECT_THAT(sets().ComputeContext(member, &owner, {member}),
+              net::SamePartyContext(SamePartyContextType::kSameParty));
+  EXPECT_THAT(sets().ComputeContext(member, &member, {owner}),
+              net::SamePartyContext(SamePartyContextType::kSameParty));
+  EXPECT_THAT(sets().ComputeContext(member, &member, {member, owner}),
+              net::SamePartyContext(SamePartyContextType::kSameParty));
+
+  EXPECT_THAT(sets().ComputeContext(nonmember, &member, {member}),
+              net::SamePartyContext(SamePartyContextType::kCrossParty));
+  EXPECT_THAT(sets().ComputeContext(member, &nonmember, {member}),
+              net::SamePartyContext(SamePartyContextType::kCrossParty));
+
+  // Top&resource differs from Ancestors.
+  EXPECT_THAT(sets().ComputeContext(member, &member, {nonmember}),
+              net::SamePartyContext(SamePartyContextType::kCrossParty,
+                                    SamePartyContextType::kCrossParty,
+                                    SamePartyContextType::kSameParty));
+
+  // Metrics values infer singleton sets when appropriate.
+  EXPECT_THAT(sets().ComputeContext(nonmember, &nonmember, {nonmember}),
+              net::SamePartyContext(SamePartyContextType::kCrossParty,
+                                    SamePartyContextType::kSameParty,
+                                    SamePartyContextType::kSameParty));
+  EXPECT_THAT(sets().ComputeContext(nonmember, &nonmember1, {nonmember}),
+              net::SamePartyContext(SamePartyContextType::kCrossParty));
+  EXPECT_THAT(sets().ComputeContext(nonmember1, &nonmember, {nonmember}),
+              net::SamePartyContext(SamePartyContextType::kCrossParty));
+  EXPECT_THAT(sets().ComputeContext(nonmember, &nonmember, {nonmember1}),
+              net::SamePartyContext(SamePartyContextType::kCrossParty,
+                                    SamePartyContextType::kCrossParty,
+                                    SamePartyContextType::kSameParty));
+
+  EXPECT_THAT(sets().ComputeContext(member, &member, {member, nonmember}),
+              net::SamePartyContext(SamePartyContextType::kCrossParty,
+                                    SamePartyContextType::kCrossParty,
+                                    SamePartyContextType::kSameParty));
+  EXPECT_THAT(sets().ComputeContext(nonmember, &nonmember, {member, nonmember}),
+              net::SamePartyContext(SamePartyContextType::kCrossParty,
+                                    SamePartyContextType::kCrossParty,
+                                    SamePartyContextType::kSameParty));
+}
+
 TEST_F(FirstPartySetsTest, IsInNontrivialFirstPartySet) {
   EXPECT_TRUE(sets().IsInNontrivialFirstPartySet(
       net::SchemefulSite(GURL("https://example.test"))));
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index 0d89a57..e8365e5 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -3892,10 +3892,10 @@
   EXPECT_FALSE(
       network_context->url_request_context()
           ->network_delegate()
-          ->ForcePrivacyMode(
-              GURL("http://foo.com"), net::SiteForCookies::FromUrl(kOtherURL),
-              url::Origin::Create(kOtherURL),
-              net::CookieOptions::SamePartyCookieContextType::kCrossParty));
+          ->ForcePrivacyMode(GURL("http://foo.com"),
+                             net::SiteForCookies::FromUrl(kOtherURL),
+                             url::Origin::Create(kOtherURL),
+                             net::SamePartyContext::Type::kCrossParty));
 }
 
 TEST_F(NetworkContextTest, PrivacyModeEnabledIfCookiesBlocked) {
@@ -3907,20 +3907,18 @@
 
   SetContentSetting(kURL, kOtherURL, CONTENT_SETTING_BLOCK,
                     network_context.get());
-  EXPECT_TRUE(
-      network_context->url_request_context()
-          ->network_delegate()
-          ->ForcePrivacyMode(
-              kURL, net::SiteForCookies::FromUrl(kOtherURL),
-              url::Origin::Create(kOtherURL),
-              net::CookieOptions::SamePartyCookieContextType::kCrossParty));
+  EXPECT_TRUE(network_context->url_request_context()
+                  ->network_delegate()
+                  ->ForcePrivacyMode(kURL,
+                                     net::SiteForCookies::FromUrl(kOtherURL),
+                                     url::Origin::Create(kOtherURL),
+                                     net::SamePartyContext::Type::kCrossParty));
   EXPECT_FALSE(
       network_context->url_request_context()
           ->network_delegate()
-          ->ForcePrivacyMode(
-              kOtherURL, net::SiteForCookies::FromUrl(kURL),
-              url::Origin::Create(kURL),
-              net::CookieOptions::SamePartyCookieContextType::kCrossParty));
+          ->ForcePrivacyMode(kOtherURL, net::SiteForCookies::FromUrl(kURL),
+                             url::Origin::Create(kURL),
+                             net::SamePartyContext::Type::kCrossParty));
 }
 
 TEST_F(NetworkContextTest, PrivacyModeDisabledIfCookiesAllowed) {
@@ -3935,10 +3933,9 @@
   EXPECT_FALSE(
       network_context->url_request_context()
           ->network_delegate()
-          ->ForcePrivacyMode(
-              kURL, net::SiteForCookies::FromUrl(kOtherURL),
-              url::Origin::Create(kOtherURL),
-              net::CookieOptions::SamePartyCookieContextType::kCrossParty));
+          ->ForcePrivacyMode(kURL, net::SiteForCookies::FromUrl(kOtherURL),
+                             url::Origin::Create(kOtherURL),
+                             net::SamePartyContext::Type::kCrossParty));
 }
 
 TEST_F(NetworkContextTest, PrivacyModeDisabledIfCookiesSettingForOtherURL) {
@@ -3954,10 +3951,9 @@
   EXPECT_FALSE(
       network_context->url_request_context()
           ->network_delegate()
-          ->ForcePrivacyMode(
-              kURL, net::SiteForCookies::FromUrl(kOtherURL),
-              url::Origin::Create(kOtherURL),
-              net::CookieOptions::SamePartyCookieContextType::kCrossParty));
+          ->ForcePrivacyMode(kURL, net::SiteForCookies::FromUrl(kOtherURL),
+                             url::Origin::Create(kOtherURL),
+                             net::SamePartyContext::Type::kCrossParty));
 }
 
 TEST_F(NetworkContextTest, PrivacyModeEnabledIfThirdPartyCookiesBlocked) {
@@ -3974,18 +3970,18 @@
   network_context->cookie_manager()->BlockThirdPartyCookies(true);
   EXPECT_TRUE(delegate->ForcePrivacyMode(
       kURL, net::SiteForCookies::FromUrl(kOtherURL), kOtherOrigin,
-      net::CookieOptions::SamePartyCookieContextType::kCrossParty));
+      net::SamePartyContext::Type::kCrossParty));
   EXPECT_FALSE(delegate->ForcePrivacyMode(
       kURL, net::SiteForCookies::FromUrl(kURL), kOrigin,
-      net::CookieOptions::SamePartyCookieContextType::kSameParty));
+      net::SamePartyContext::Type::kSameParty));
 
   network_context->cookie_manager()->BlockThirdPartyCookies(false);
   EXPECT_FALSE(delegate->ForcePrivacyMode(
       kURL, net::SiteForCookies::FromUrl(kOtherURL), kOtherOrigin,
-      net::CookieOptions::SamePartyCookieContextType::kCrossParty));
+      net::SamePartyContext::Type::kCrossParty));
   EXPECT_FALSE(delegate->ForcePrivacyMode(
       kURL, net::SiteForCookies::FromUrl(kURL), kOrigin,
-      net::CookieOptions::SamePartyCookieContextType::kSameParty));
+      net::SamePartyContext::Type::kSameParty));
 }
 
 TEST_F(NetworkContextTest, CanSetCookieFalseIfCookiesBlocked) {
diff --git a/services/network/network_service_network_delegate.cc b/services/network/network_service_network_delegate.cc
index ad69c8d7..ce26781 100644
--- a/services/network/network_service_network_delegate.cc
+++ b/services/network/network_service_network_delegate.cc
@@ -15,6 +15,7 @@
 #include "net/base/isolation_info.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
+#include "net/cookies/same_party_context.h"
 #include "net/url_request/referrer_policy.h"
 #include "net/url_request/url_request.h"
 #include "services/network/cookie_manager.h"
@@ -254,12 +255,11 @@
     const GURL& url,
     const net::SiteForCookies& site_for_cookies,
     const absl::optional<url::Origin>& top_frame_origin,
-    net::CookieOptions::SamePartyCookieContextType
-        same_party_cookie_context_type) const {
+    net::SamePartyContext::Type same_party_context_type) const {
   return network_context_->cookie_manager()
       ->cookie_settings()
       .IsPrivacyModeEnabled(url, site_for_cookies.RepresentativeUrl(),
-                            top_frame_origin, same_party_cookie_context_type);
+                            top_frame_origin, same_party_context_type);
 }
 
 bool NetworkServiceNetworkDelegate::
diff --git a/services/network/network_service_network_delegate.h b/services/network/network_service_network_delegate.h
index f9a94849..3ea118d2 100644
--- a/services/network/network_service_network_delegate.h
+++ b/services/network/network_service_network_delegate.h
@@ -11,6 +11,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/completion_once_callback.h"
 #include "net/base/network_delegate_impl.h"
+#include "net/cookies/same_party_context.h"
 #include "services/network/network_context.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -64,11 +65,11 @@
                       const net::CanonicalCookie& cookie,
                       net::CookieOptions* options,
                       bool allowed_from_caller) override;
-  bool OnForcePrivacyMode(const GURL& url,
-                          const net::SiteForCookies& site_for_cookies,
-                          const absl::optional<url::Origin>& top_frame_origin,
-                          net::CookieOptions::SamePartyCookieContextType
-                              same_party_cookie_context_type) const override;
+  bool OnForcePrivacyMode(
+      const GURL& url,
+      const net::SiteForCookies& site_for_cookies,
+      const absl::optional<url::Origin>& top_frame_origin,
+      net::SamePartyContext::Type same_party_context_type) const override;
   bool OnCancelURLRequestWithPolicyViolatingReferrerHeader(
       const net::URLRequest& request,
       const GURL& target_url,
diff --git a/services/network/public/cpp/cookie_manager_mojom_traits.cc b/services/network/public/cpp/cookie_manager_mojom_traits.cc
index 24fe201..4721b6f7 100644
--- a/services/network/public/cpp/cookie_manager_mojom_traits.cc
+++ b/services/network/public/cpp/cookie_manager_mojom_traits.cc
@@ -5,6 +5,7 @@
 #include "services/network/public/cpp/cookie_manager_mojom_traits.h"
 
 #include "mojo/public/cpp/base/time_mojom_traits.h"
+#include "net/cookies/same_party_context.h"
 
 namespace mojo {
 
@@ -338,15 +339,15 @@
 }
 
 bool EnumTraits<network::mojom::SamePartyCookieContextType,
-                net::CookieOptions::SamePartyCookieContextType>::
+                net::SamePartyContext::Type>::
     FromMojom(network::mojom::SamePartyCookieContextType context_type,
-              net::CookieOptions::SamePartyCookieContextType* out) {
+              net::SamePartyContext::Type* out) {
   switch (context_type) {
     case network::mojom::SamePartyCookieContextType::kCrossParty:
-      *out = net::CookieOptions::SamePartyCookieContextType::kCrossParty;
+      *out = net::SamePartyContext::Type::kCrossParty;
       return true;
     case network::mojom::SamePartyCookieContextType::kSameParty:
-      *out = net::CookieOptions::SamePartyCookieContextType::kSameParty;
+      *out = net::SamePartyContext::Type::kSameParty;
       return true;
   }
   return false;
@@ -354,12 +355,12 @@
 
 network::mojom::SamePartyCookieContextType
 EnumTraits<network::mojom::SamePartyCookieContextType,
-           net::CookieOptions::SamePartyCookieContextType>::
-    ToMojom(net::CookieOptions::SamePartyCookieContextType context_type) {
+           net::SamePartyContext::Type>::ToMojom(net::SamePartyContext::Type
+                                                     context_type) {
   switch (context_type) {
-    case net::CookieOptions::SamePartyCookieContextType::kCrossParty:
+    case net::SamePartyContext::Type::kCrossParty:
       return network::mojom::SamePartyCookieContextType::kCrossParty;
-    case net::CookieOptions::SamePartyCookieContextType::kSameParty:
+    case net::SamePartyContext::Type::kSameParty:
       return network::mojom::SamePartyCookieContextType::kSameParty;
   }
   NOTREACHED();
@@ -389,12 +390,10 @@
   else
     cookie_options->unset_return_excluded_cookies();
 
-  net::CookieOptions::SamePartyCookieContextType same_party_cookie_context_type;
-  if (!mojo_options.ReadSamePartyCookieContextType(
-          &same_party_cookie_context_type))
+  net::SamePartyContext same_party_context;
+  if (!mojo_options.ReadSamePartyContext(&same_party_context))
     return false;
-  cookie_options->set_same_party_cookie_context_type(
-      same_party_cookie_context_type);
+  cookie_options->set_same_party_context(same_party_context);
 
   cookie_options->set_full_party_context_size(
       mojo_options.full_party_context_size());
@@ -546,4 +545,25 @@
   return true;
 }
 
+bool StructTraits<network::mojom::SamePartyContextDataView,
+                  net::SamePartyContext>::
+    Read(network::mojom::SamePartyContextDataView context,
+         net::SamePartyContext* out) {
+  net::SamePartyContext::Type context_type;
+  if (!context.ReadContextType(&context_type))
+    return false;
+
+  net::SamePartyContext::Type ancestors_for_metrics_only;
+  if (!context.ReadAncestorsForMetricsOnly(&ancestors_for_metrics_only))
+    return false;
+
+  net::SamePartyContext::Type top_resource_for_metrics_only;
+  if (!context.ReadTopResourceForMetricsOnly(&top_resource_for_metrics_only))
+    return false;
+
+  *out = net::SamePartyContext(context_type, ancestors_for_metrics_only,
+                               top_resource_for_metrics_only);
+  return true;
+}
+
 }  // namespace mojo
diff --git a/services/network/public/cpp/cookie_manager_mojom_traits.h b/services/network/public/cpp/cookie_manager_mojom_traits.h
index 3d57761..0f1dacf5 100644
--- a/services/network/public/cpp/cookie_manager_mojom_traits.h
+++ b/services/network/public/cpp/cookie_manager_mojom_traits.h
@@ -12,6 +12,7 @@
 #include "net/cookies/cookie_constants.h"
 #include "net/cookies/cookie_inclusion_status.h"
 #include "net/cookies/cookie_options.h"
+#include "net/cookies/same_party_context.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -119,12 +120,12 @@
 
 template <>
 struct EnumTraits<network::mojom::SamePartyCookieContextType,
-                  net::CookieOptions::SamePartyCookieContextType> {
+                  net::SamePartyContext::Type> {
   static network::mojom::SamePartyCookieContextType ToMojom(
-      net::CookieOptions::SamePartyCookieContextType context_type);
+      net::SamePartyContext::Type context_type);
 
   static bool FromMojom(network::mojom::SamePartyCookieContextType context_type,
-                        net::CookieOptions::SamePartyCookieContextType* out);
+                        net::SamePartyContext::Type* out);
 };
 
 template <>
@@ -143,9 +144,8 @@
     return o.return_excluded_cookies();
   }
 
-  static net::CookieOptions::SamePartyCookieContextType
-  same_party_cookie_context_type(const net::CookieOptions& o) {
-    return o.same_party_cookie_context_type();
+  static net::SamePartyContext same_party_context(const net::CookieOptions& o) {
+    return o.same_party_context();
   }
 
   static uint32_t full_party_context_size(const net::CookieOptions& o) {
@@ -293,6 +293,26 @@
                    net::CookieChangeInfo* out);
 };
 
+template <>
+struct StructTraits<network::mojom::SamePartyContextDataView,
+                    net::SamePartyContext> {
+  static net::SamePartyContext::Type context_type(
+      const net::SamePartyContext& s) {
+    return s.context_type();
+  }
+  static net::SamePartyContext::Type ancestors_for_metrics_only(
+      const net::SamePartyContext& s) {
+    return s.ancestors_for_metrics_only();
+  }
+  static net::SamePartyContext::Type top_resource_for_metrics_only(
+      const net::SamePartyContext& s) {
+    return s.top_resource_for_metrics_only();
+  }
+
+  static bool Read(network::mojom::SamePartyContextDataView bundle,
+                   net::SamePartyContext* out);
+};
+
 }  // namespace mojo
 
 #endif  // SERVICES_NETWORK_PUBLIC_CPP_COOKIE_MANAGER_MOJOM_TRAITS_H_
diff --git a/services/network/public/cpp/cookie_manager_mojom_traits_unittest.cc b/services/network/public/cpp/cookie_manager_mojom_traits_unittest.cc
index a53ea1e..8440e07 100644
--- a/services/network/public/cpp/cookie_manager_mojom_traits_unittest.cc
+++ b/services/network/public/cpp/cookie_manager_mojom_traits_unittest.cc
@@ -315,7 +315,7 @@
 }
 
 TEST(CookieManagerTraitsTest, Roundtrips_SamePartyCookieContextType) {
-  using ContextType = net::CookieOptions::SamePartyCookieContextType;
+  using ContextType = net::SamePartyContext::Type;
   for (ContextType context_type :
        {ContextType::kCrossParty, ContextType::kSameParty}) {
     ContextType roundtrip;
@@ -343,8 +343,12 @@
             net::CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE),
         copy.same_site_cookie_context());
     EXPECT_TRUE(copy.return_excluded_cookies());
-    EXPECT_EQ(net::CookieOptions::SamePartyCookieContextType::kCrossParty,
-              copy.same_party_cookie_context_type());
+    EXPECT_EQ(net::SamePartyContext::Type::kCrossParty,
+              copy.same_party_context().context_type());
+    EXPECT_EQ(net::SamePartyContext::Type::kCrossParty,
+              copy.same_party_context().ancestors_for_metrics_only());
+    EXPECT_EQ(net::SamePartyContext::Type::kCrossParty,
+              copy.same_party_context().top_resource_for_metrics_only());
     EXPECT_EQ(10u, copy.full_party_context_size());
     EXPECT_TRUE(copy.is_in_nontrivial_first_party_set());
   }
@@ -354,8 +358,8 @@
     very_trusted.set_include_httponly();
     very_trusted.set_same_site_cookie_context(
         net::CookieOptions::SameSiteCookieContext::MakeInclusive());
-    very_trusted.set_same_party_cookie_context_type(
-        net::CookieOptions::SamePartyCookieContextType::kSameParty);
+    very_trusted.set_same_party_context(
+        net::SamePartyContext(net::SamePartyContext::Type::kSameParty));
     very_trusted.set_full_party_context_size(1u);
     very_trusted.set_is_in_nontrivial_first_party_set(true);
 
@@ -365,8 +369,12 @@
     EXPECT_EQ(net::CookieOptions::SameSiteCookieContext::MakeInclusive(),
               copy.same_site_cookie_context());
     EXPECT_FALSE(copy.return_excluded_cookies());
-    EXPECT_EQ(net::CookieOptions::SamePartyCookieContextType::kSameParty,
-              copy.same_party_cookie_context_type());
+    EXPECT_EQ(net::SamePartyContext::Type::kSameParty,
+              copy.same_party_context().context_type());
+    EXPECT_EQ(net::SamePartyContext::Type::kSameParty,
+              copy.same_party_context().ancestors_for_metrics_only());
+    EXPECT_EQ(net::SamePartyContext::Type::kSameParty,
+              copy.same_party_context().top_resource_for_metrics_only());
     EXPECT_EQ(1u, copy.full_party_context_size());
     EXPECT_TRUE(copy.is_in_nontrivial_first_party_set());
   }
diff --git a/services/network/public/mojom/cookie_manager.mojom b/services/network/public/mojom/cookie_manager.mojom
index 949d2be..cd9dd4e 100644
--- a/services/network/public/mojom/cookie_manager.mojom
+++ b/services/network/public/mojom/cookie_manager.mojom
@@ -133,7 +133,7 @@
   CookieSameSiteContext same_site_cookie_context;
   bool update_access_time = true;
   bool return_excluded_cookies = false;
-  SamePartyCookieContextType same_party_cookie_context_type = kCrossParty;
+  SamePartyContext same_party_context;
   // The size of the isolation_info.party_context plus the top-frame site for
   // logging purposes.
   uint32 full_party_context_size = 0;
@@ -200,6 +200,13 @@
   CookieAccessResult access_result;
 };
 
+// Keep defaults in here in sync with net/cookies/same_party_context.cc.
+struct SamePartyContext {
+  SamePartyCookieContextType context_type = kCrossParty;
+  SamePartyCookieContextType ancestors_for_metrics_only = kCrossParty;
+  SamePartyCookieContextType top_resource_for_metrics_only = kCrossParty;
+};
+
 // Keep values here in sync with net::CookieChangeCause.
 enum CookieChangeCause {
   // The cookie was inserted.
diff --git a/services/network/restricted_cookie_manager.cc b/services/network/restricted_cookie_manager.cc
index 56b1297..27c25107 100644
--- a/services/network/restricted_cookie_manager.cc
+++ b/services/network/restricted_cookie_manager.cc
@@ -69,10 +69,9 @@
   // SameParty cookies for requests in same-party contexts embedded in top-level
   // extension frames.
   bool force_ignore_top_frame_party = false;
-  options.set_same_party_cookie_context_type(
-      net::cookie_util::ComputeSamePartyContext(request_site, isolation_info,
-                                                cookie_access_delegate,
-                                                force_ignore_top_frame_party));
+  options.set_same_party_context(net::cookie_util::ComputeSamePartyContext(
+      request_site, isolation_info, cookie_access_delegate,
+      force_ignore_top_frame_party));
   if (isolation_info.party_context().has_value()) {
     // Count the top-frame site since it's not in the party_context.
     options.set_full_party_context_size(isolation_info.party_context()->size() +
@@ -120,10 +119,9 @@
   }
   net::SchemefulSite request_site(url);
   bool force_ignore_top_frame_party = false;
-  options.set_same_party_cookie_context_type(
-      net::cookie_util::ComputeSamePartyContext(request_site, isolation_info,
-                                                cookie_access_delegate,
-                                                force_ignore_top_frame_party));
+  options.set_same_party_context(net::cookie_util::ComputeSamePartyContext(
+      request_site, isolation_info, cookie_access_delegate,
+      force_ignore_top_frame_party));
   if (isolation_info.party_context().has_value()) {
     // Count the top-frame site since it's not in the party_context.
     options.set_full_party_context_size(isolation_info.party_context()->size() +
diff --git a/services/network/restricted_cookie_manager_unittest.cc b/services/network/restricted_cookie_manager_unittest.cc
index bfcb9983..0e65d9b 100644
--- a/services/network/restricted_cookie_manager_unittest.cc
+++ b/services/network/restricted_cookie_manager_unittest.cc
@@ -27,6 +27,7 @@
 #include "net/cookies/cookie_store.h"
 #include "net/cookies/cookie_store_test_callbacks.h"
 #include "net/cookies/cookie_util.h"
+#include "net/cookies/same_party_context.h"
 #include "net/cookies/test_cookie_access_delegate.h"
 #include "services/network/cookie_access_delegate_impl.h"
 #include "services/network/cookie_settings.h"
@@ -215,9 +216,8 @@
       const net::CookieOptions::SameSiteCookieContext::ContextType
           same_site_cookie_context_type = net::CookieOptions::
               SameSiteCookieContext::ContextType::CROSS_SITE,
-      const net::CookieOptions::SamePartyCookieContextType
-          same_party_cookie_context_type =
-              net::CookieOptions::SamePartyCookieContextType::kCrossParty) {
+      const net::SamePartyContext::Type same_party_context_type =
+          net::SamePartyContext::Type::kCrossParty) {
     net::ResultSavingCookieCallback<net::CookieAccessResult> callback;
     net::CookieOptions options;
     if (can_modify_httponly)
@@ -225,7 +225,8 @@
     net::CookieOptions::SameSiteCookieContext same_site_cookie_context(
         same_site_cookie_context_type, same_site_cookie_context_type);
     options.set_same_site_cookie_context(same_site_cookie_context);
-    options.set_same_party_cookie_context_type(same_party_cookie_context_type);
+    options.set_same_party_context(
+        net::SamePartyContext(same_party_context_type));
 
     cookie_monster_.SetCanonicalCookieAsync(
         std::make_unique<net::CanonicalCookie>(cookie),
@@ -346,7 +347,7 @@
             net::COOKIE_PRIORITY_DEFAULT, /* same_party = */ true),
         "https", /* can_modify_httponly = */ true,
         net::CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX,
-        net::CookieOptions::SamePartyCookieContextType::kSameParty));
+        net::SamePartyContext::Type::kSameParty));
   }
 
  private:
@@ -596,13 +597,19 @@
             net::MatchesCookieNameValue("cookie-name", "cookie-value")));
   }
 
-  EXPECT_THAT(recorded_activity(),
-              ElementsAre(MatchesCookieOp(
-                  mojom::CookieAccessDetails::Type::kRead,
-                  "https://example.com/test/", "",
-                  CookieOrLine("cookie-name=cookie-value",
-                               mojom::CookieOrLine::Tag::COOKIE),
-                  testing::AllOf(net::IsInclude(), Not(net::ShouldWarn())))));
+  EXPECT_THAT(
+      recorded_activity(),
+      ElementsAre(MatchesCookieOp(
+          mojom::CookieAccessDetails::Type::kRead, "https://example.com/test/",
+          "",
+          CookieOrLine("cookie-name=cookie-value",
+                       mojom::CookieOrLine::Tag::COOKIE),
+          testing::AllOf(
+              net::IsInclude(),
+              net::HasExactlyWarningReasonsForTesting(
+                  std::vector<net::CookieInclusionStatus::WarningReason>{
+                      net::CookieInclusionStatus::
+                          WARN_SAMESITE_NONE_REQUIRED})))));
 
   // Disabing getting third-party cookies works correctly.
   cookie_settings_.set_block_third_party_cookies(true);
@@ -626,9 +633,9 @@
               "https://example.com/test/", "",
               CookieOrLine("cookie-name=cookie-value",
                            mojom::CookieOrLine::Tag::COOKIE),
-              net::HasExactlyExclusionReasonsForTesting(
-                  std::vector<net::CookieInclusionStatus::ExclusionReason>{
-                      net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES}))));
+              net::CookieInclusionStatus::MakeFromReasonsForTesting(
+                  {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES},
+                  {net::CookieInclusionStatus::WARN_SAMESITE_NONE_REQUIRED}))));
 }
 
 TEST_P(RestrictedCookieManagerTest, GetAllForUrlPolicyWarnActual) {
@@ -685,52 +692,56 @@
             net::MatchesCookieNameValue("cookie-name", "cookie-value")));
   }
   // Same Party. `party_context` contains fps site.
-    service_->OverrideIsolationInfoForTesting(net::IsolationInfo::Create(
-        net::IsolationInfo::RequestType::kOther, kDefaultOrigin, kDefaultOrigin,
-        net::SiteForCookies(),
-        std::set<net::SchemefulSite>{
-            net::SchemefulSite(GURL("https://member1.com"))}));
-    {
-      auto options = mojom::CookieManagerGetOptions::New();
-      options->name = "cookie-name";
-      options->match_type = mojom::CookieMatchType::STARTS_WITH;
+  service_->OverrideIsolationInfoForTesting(net::IsolationInfo::Create(
+      net::IsolationInfo::RequestType::kOther, kDefaultOrigin, kDefaultOrigin,
+      net::SiteForCookies(),
+      std::set<net::SchemefulSite>{
+          net::SchemefulSite(GURL("https://member1.com"))}));
+  {
+    auto options = mojom::CookieManagerGetOptions::New();
+    options->name = "cookie-name";
+    options->match_type = mojom::CookieMatchType::STARTS_WITH;
 
-      EXPECT_THAT(sync_service_->GetAllForUrl(
-                      kDefaultUrlWithPath, net::SiteForCookies(),
-                      kDefaultOrigin, std::move(options)),
-                  ElementsAre(net::MatchesCookieNameValue("cookie-name",
-                                                          "cookie-value")));
-    }
-    {
-      // Should still be blocked when third-party cookie blocking is enabled.
-      cookie_settings_.set_block_third_party_cookies(true);
-      auto options = mojom::CookieManagerGetOptions::New();
-      options->name = "cookie-name";
-      options->match_type = mojom::CookieMatchType::STARTS_WITH;
+    EXPECT_THAT(
+        sync_service_->GetAllForUrl(kDefaultUrlWithPath, net::SiteForCookies(),
+                                    kDefaultOrigin, std::move(options)),
+        ElementsAre(
+            net::MatchesCookieNameValue("cookie-name", "cookie-value")));
+  }
+  {
+    // Should still be blocked when third-party cookie blocking is enabled.
+    cookie_settings_.set_block_third_party_cookies(true);
+    auto options = mojom::CookieManagerGetOptions::New();
+    options->name = "cookie-name";
+    options->match_type = mojom::CookieMatchType::STARTS_WITH;
 
-      EXPECT_THAT(sync_service_->GetAllForUrl(
-                      kDefaultUrlWithPath, net::SiteForCookies(),
-                      kDefaultOrigin, std::move(options)),
-                  IsEmpty());
+    EXPECT_THAT(
+        sync_service_->GetAllForUrl(kDefaultUrlWithPath, net::SiteForCookies(),
+                                    kDefaultOrigin, std::move(options)),
+        IsEmpty());
 
-      // This checks that the cookie access is not double-reported due the
-      // warning reason and EXCLUDE_USER_PREFERENCES.
-      EXPECT_THAT(
-          recorded_activity(),
-          ElementsAre(
-              testing::_, testing::_,
-              MatchesCookieOp(
-                  mojom::CookieAccessDetails::Type::kRead, kDefaultUrlWithPath,
-                  "",
-                  CookieOrLine("cookie-name=cookie-value",
-                               mojom::CookieOrLine::Tag::COOKIE),
-                  net::CookieInclusionStatus::MakeFromReasonsForTesting(
-                      {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES},
-                      {net::CookieInclusionStatus::
-                           WARN_TREATED_AS_SAMEPARTY}))));
+    // This checks that the cookie access is not double-reported due the
+    // warning reason and EXCLUDE_USER_PREFERENCES.
+    std::vector<net::CookieInclusionStatus::WarningReason> expected_warnings = {
+        net::CookieInclusionStatus::WARN_TREATED_AS_SAMEPARTY,
+        net::CookieInclusionStatus::
+            WARN_SAMESITE_NONE_INCLUDED_BY_SAMEPARTY_ANCESTORS,
+    };
+    EXPECT_THAT(
+        recorded_activity(),
+        ElementsAre(
+            testing::_, testing::_,
+            MatchesCookieOp(
+                mojom::CookieAccessDetails::Type::kRead, kDefaultUrlWithPath,
+                "",
+                CookieOrLine("cookie-name=cookie-value",
+                             mojom::CookieOrLine::Tag::COOKIE),
+                net::CookieInclusionStatus::MakeFromReasonsForTesting(
+                    {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES},
+                    expected_warnings))));
 
-      cookie_settings_.set_block_third_party_cookies(false);
-    }
+    cookie_settings_.set_block_third_party_cookies(false);
+  }
 
   // Cross Party
   {
@@ -885,7 +896,12 @@
       ElementsAre(MatchesCookieOp(
           mojom::CookieAccessDetails::Type::kChange, "https://example.com/", "",
           CookieOrLine("A=B", mojom::CookieOrLine::Tag::COOKIE),
-          testing::AllOf(net::IsInclude(), Not(net::ShouldWarn())))));
+          testing::AllOf(
+              net::IsInclude(),
+              net::HasExactlyWarningReasonsForTesting(
+                  std::vector<net::CookieInclusionStatus::WarningReason>{
+                      net::CookieInclusionStatus::
+                          WARN_SAMESITE_NONE_REQUIRED})))));
 
   {
     // Not if third-party cookies are disabled, though.
diff --git a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc
index 576df0c..5dff215 100644
--- a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc
+++ b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc
@@ -343,7 +343,7 @@
     const base::RepeatingClosure& sample_callback_for_testing)
     : sampled_thread_id_(sampled_thread_id),
       trace_writer_(std::move(trace_writer)),
-      should_enable_filtering_(should_enable_filtering),
+      stack_profile_writer_(should_enable_filtering),
       sample_callback_for_testing_(sample_callback_for_testing) {}
 
 TracingSamplerProfiler::TracingProfileBuilder::~TracingProfileBuilder() {
@@ -403,12 +403,7 @@
   }
 
   if (reset_incremental_state_) {
-    interned_callstacks_.ResetEmittedState();
-    interned_frames_.ResetEmittedState();
-    interned_frame_names_.ResetEmittedState();
-    interned_module_names_.ResetEmittedState();
-    interned_module_ids_.ResetEmittedState();
-    interned_modules_.ResetEmittedState();
+    stack_profile_writer_.ResetEmittedState();
 
     auto trace_packet = trace_writer_->NewTracePacket();
     trace_packet->set_sequence_flags(
@@ -431,7 +426,8 @@
   // Delta encoded timestamps and interned data require incremental state.
   trace_packet->set_sequence_flags(
       perfetto::protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
-  auto callstack_id = GetCallstackIDAndMaybeEmit(frames, &trace_packet);
+  auto callstack_id =
+      stack_profile_writer_.GetCallstackIDAndMaybeEmit(frames, &trace_packet);
   auto* streaming_profile_packet = trace_packet->set_streaming_profile_packet();
   streaming_profile_packet->add_callstack_iid(callstack_id);
 
@@ -451,8 +447,13 @@
   trace_writer_ = std::move(writer);
 }
 
+TracingSamplerProfiler::StackProfileWriter::StackProfileWriter(
+    bool enable_filtering)
+    : should_enable_filtering_(enable_filtering) {}
+TracingSamplerProfiler::StackProfileWriter::~StackProfileWriter() = default;
+
 InterningID
-TracingSamplerProfiler::TracingProfileBuilder::GetCallstackIDAndMaybeEmit(
+TracingSamplerProfiler::StackProfileWriter::GetCallstackIDAndMaybeEmit(
     const std::vector<base::Frame>& frames,
     perfetto::TraceWriter::TracePacketHandle* trace_packet) {
   size_t ip_hash = 0;
@@ -584,6 +585,15 @@
   return interned_callstack.id;
 }
 
+void TracingSamplerProfiler::StackProfileWriter::ResetEmittedState() {
+  interned_callstacks_.ResetEmittedState();
+  interned_frames_.ResetEmittedState();
+  interned_frame_names_.ResetEmittedState();
+  interned_module_names_.ResetEmittedState();
+  interned_module_ids_.ResetEmittedState();
+  interned_modules_.ResetEmittedState();
+}
+
 // static
 void TracingSamplerProfiler::MangleModuleIDIfNeeded(std::string* module_id) {
 #if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS)
diff --git a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h
index e01e049..1eee5776 100644
--- a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h
+++ b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h
@@ -58,6 +58,36 @@
 // field |profiler_| to be thread-safe.
 class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler {
  public:
+  class StackProfileWriter {
+   public:
+    explicit StackProfileWriter(bool should_enable_filtering);
+    ~StackProfileWriter();
+
+    StackProfileWriter(const StackProfileWriter&) = delete;
+    StackProfileWriter& operator=(const StackProfileWriter&) = delete;
+
+    InterningID GetCallstackIDAndMaybeEmit(
+        const std::vector<base::Frame>& frames,
+        perfetto::TraceWriter::TracePacketHandle* trace_packet);
+
+    void ResetEmittedState();
+
+   private:
+    const bool should_enable_filtering_;
+    InterningIndex<TypeList<size_t>, SizeList<1024>> interned_callstacks_{};
+    InterningIndex<TypeList<std::pair<std::string, std::string>,
+                            std::pair<uintptr_t, std::string>>,
+                   SizeList<1024, 1024>>
+        interned_frames_{};
+    InterningIndex<TypeList<std::string>, SizeList<1024>>
+        interned_frame_names_{};
+    InterningIndex<TypeList<std::string>, SizeList<1024>>
+        interned_module_names_{};
+    InterningIndex<TypeList<std::string>, SizeList<1024>>
+        interned_module_ids_{};
+    InterningIndex<TypeList<uintptr_t>, SizeList<1024>> interned_modules_{};
+  };
+
   // This class will receive the sampling profiler stackframes and output them
   // to the chrome trace via an event. Exposed for testing.
   class COMPONENT_EXPORT(TRACING_CPP) TracingProfileBuilder
@@ -92,9 +122,6 @@
       DISALLOW_COPY_AND_ASSIGN(BufferedSample);
     };
 
-    InterningID GetCallstackIDAndMaybeEmit(
-        const std::vector<base::Frame>& frames,
-        perfetto::TraceWriter::TracePacketHandle* trace_packet);
     void WriteSampleToTrace(const BufferedSample& sample);
 
     // We usually sample at 50ms, and expect that tracing should have started in
@@ -106,22 +133,10 @@
     const base::PlatformThreadId sampled_thread_id_;
     base::Lock trace_writer_lock_;
     std::unique_ptr<perfetto::TraceWriter> trace_writer_;
-    InterningIndex<TypeList<size_t>, SizeList<1024>> interned_callstacks_{};
-    InterningIndex<TypeList<std::pair<std::string, std::string>,
-                            std::pair<uintptr_t, std::string>>,
-                   SizeList<1024, 1024>>
-        interned_frames_{};
-    InterningIndex<TypeList<std::string>, SizeList<1024>>
-        interned_frame_names_{};
-    InterningIndex<TypeList<std::string>, SizeList<1024>>
-        interned_module_names_{};
-    InterningIndex<TypeList<std::string>, SizeList<1024>>
-        interned_module_ids_{};
-    InterningIndex<TypeList<uintptr_t>, SizeList<1024>> interned_modules_{};
+    StackProfileWriter stack_profile_writer_;
     bool reset_incremental_state_ = true;
     uint32_t last_incremental_state_reset_id_ = 0;
     base::TimeTicks last_timestamp_;
-    const bool should_enable_filtering_;
     base::RepeatingClosure sample_callback_for_testing_;
   };
 
diff --git a/services/viz/privileged/mojom/gl/info_collection_gpu_service.mojom b/services/viz/privileged/mojom/gl/info_collection_gpu_service.mojom
index c58d6f4..8d434e0 100644
--- a/services/viz/privileged/mojom/gl/info_collection_gpu_service.mojom
+++ b/services/viz/privileged/mojom/gl/info_collection_gpu_service.mojom
@@ -23,6 +23,7 @@
   [EnableIf=is_win]
   GetGpuSupportedDx12VersionAndDevicePerfInfo()
       => (uint32 d3d12_feature_level,
+          uint32 highest_shader_model_version,
           gpu.mojom.DevicePerfInfo device_perf_info);
 
   // Requests Vulkan version
diff --git a/storage/browser/quota/quota_database.h b/storage/browser/quota/quota_database.h
index 87a53e2..fffdd4c 100644
--- a/storage/browser/quota/quota_database.h
+++ b/storage/browser/quota/quota_database.h
@@ -19,7 +19,7 @@
 #include "base/sequence_checker.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "base/util/type_safety/id_type.h"
+#include "base/types/id_type.h"
 #include "components/services/storage/public/cpp/buckets/bucket_id.h"
 #include "components/services/storage/public/cpp/buckets/bucket_info.h"
 #include "components/services/storage/public/cpp/quota_error_or.h"
diff --git a/styleguide/c++/c++-dos-and-donts.md b/styleguide/c++/c++-dos-and-donts.md
index 9b283a4..b5fab98 100644
--- a/styleguide/c++/c++-dos-and-donts.md
+++ b/styleguide/c++/c++-dos-and-donts.md
@@ -318,6 +318,12 @@
 // The ExpensiveStuff() call will not happen in official release builds. No need
 // to use checks for DCHECK_IS_ON() anywhere.
 DCHECK(ExpensiveStuff());
+
+std::string ExpensiveDebugMessage() { ... }  // No problem.
+
+// Calls in stream operators are also dead code in official release builds (not
+// called with the result discarded). This is fine.
+DCHECK(...) << ExpensiveDebugMessage();
 ```
 
 This code will probably do expensive things that are not needed in official
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 3292e0a..b508f97 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -4788,6 +4788,5269 @@
       }
     ]
   },
+  "android-12-x64-fyi-rel": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "absl_hardening_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "absl_hardening_tests",
+        "test_id_prefix": "ninja://third_party/abseil-cpp:absl_hardening_tests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "android_browsertests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "android_browsertests",
+        "test_id_prefix": "ninja://chrome/test:android_browsertests/"
+      },
+      {
+        "args": [
+          "--test-launcher-batch-limit=1",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "android_sync_integration_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "android_sync_integration_tests",
+        "test_id_prefix": "ninja://chrome/test:android_sync_integration_tests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "android_webview_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "android_webview_unittests",
+        "test_id_prefix": "ninja://android_webview/test:android_webview_unittests/"
+      },
+      {
+        "args": [
+          "angle_unittests",
+          "-v",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "angle_unittests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "base_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "base_unittests",
+        "test_id_prefix": "ninja://base:base_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "base_util_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "base_util_unittests",
+        "test_id_prefix": "ninja://base/util:base_util_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "blink_common_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "blink_common_unittests",
+        "test_id_prefix": "ninja://third_party/blink/common:blink_common_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "blink_heap_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "blink_heap_unittests",
+        "test_id_prefix": "ninja://third_party/blink/renderer/platform/heap:blink_heap_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "blink_platform_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "blink_platform_unittests",
+        "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_platform_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "webkit_unit_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webkit_unit_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 6
+        },
+        "test": "blink_unittests",
+        "test_id_prefix": "ninja://third_party/blink/renderer/controller:blink_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "boringssl_crypto_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "boringssl_crypto_tests",
+        "test_id_prefix": "ninja://third_party/boringssl:boringssl_crypto_tests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "boringssl_ssl_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "boringssl_ssl_tests",
+        "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "breakpad_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "breakpad_unittests",
+        "test_id_prefix": "ninja://third_party/breakpad:breakpad_unittests/"
+      },
+      {
+        "args": [
+          "--gtest_filter=-*UsingRealWebcam*",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "capture_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "capture_unittests",
+        "test_id_prefix": "ninja://media/capture:capture_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cast_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "cast_unittests",
+        "test_id_prefix": "ninja://media/cast:cast_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cc_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "cc_unittests",
+        "test_id_prefix": "ninja://cc:cc_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "chrome_public_smoke_test"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "chrome_public_smoke_test",
+        "test_id_prefix": "ninja://chrome/android:chrome_public_smoke_test/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb",
+          "--git-revision=${got_revision}"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "chrome_public_test_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chrome-gold@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 20
+        },
+        "test": "chrome_public_test_apk",
+        "test_id_prefix": "ninja://chrome/android:chrome_public_test_apk/"
+      },
+      {
+        "args": [
+          "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_cardboard_skipdon_setupcomplete.json",
+          "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "chrome_public_test_vr_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "chrome_public_test_vr_apk",
+        "test_id_prefix": "ninja://chrome/android:chrome_public_test_vr_apk/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "components_browsertests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "components_browsertests",
+        "test_id_prefix": "ninja://components:components_browsertests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "components_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 4
+        },
+        "test": "components_unittests",
+        "test_id_prefix": "ninja://components:components_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "content_browsertests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 9
+        },
+        "test": "content_browsertests",
+        "test_id_prefix": "ninja://content/test:content_browsertests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "content_shell_test_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
+        },
+        "test": "content_shell_test_apk",
+        "test_id_prefix": "ninja://content/shell/android:content_shell_test_apk/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "content_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
+        },
+        "test": "content_unittests",
+        "test_id_prefix": "ninja://content/test:content_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "crashpad_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "crashpad_tests",
+        "test_id_prefix": "ninja://third_party/crashpad/crashpad:crashpad_tests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "crypto_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "crypto_unittests",
+        "test_id_prefix": "ninja://crypto:crypto_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "device_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "device_unittests",
+        "test_id_prefix": "ninja://device:device_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "display_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "display_unittests",
+        "test_id_prefix": "ninja://ui/display:display_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "events_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "events_unittests",
+        "test_id_prefix": "ninja://ui/events:events_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "gcm_unit_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gcm_unit_tests",
+        "test_id_prefix": "ninja://google_apis/gcm:gcm_unit_tests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "gfx_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gfx_unittests",
+        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "gin_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gin_unittests",
+        "test_id_prefix": "ninja://gin:gin_unittests/"
+      },
+      {
+        "args": [
+          "--use-cmd-decoder=validating",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "gl_tests_validating"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "gl_tests_validating",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_tests",
+        "test_id_prefix": "ninja://gpu:gl_tests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "gl_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_unittests",
+        "test_id_prefix": "ninja://ui/gl:gl_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "google_apis_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "google_apis_unittests",
+        "test_id_prefix": "ninja://google_apis:google_apis_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "gpu_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gpu_unittests",
+        "test_id_prefix": "ninja://gpu:gpu_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "gwp_asan_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gwp_asan_unittests",
+        "test_id_prefix": "ninja://components/gwp_asan:gwp_asan_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "ipc_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "ipc_tests",
+        "test_id_prefix": "ninja://ipc:ipc_tests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "jingle_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "jingle_unittests",
+        "test_id_prefix": "ninja://jingle:jingle_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "latency_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "latency_unittests",
+        "test_id_prefix": "ninja://ui/latency:latency_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "libjingle_xmpp_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "libjingle_xmpp_unittests",
+        "test_id_prefix": "ninja://third_party/libjingle_xmpp:libjingle_xmpp_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "liburlpattern_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "liburlpattern_unittests",
+        "test_id_prefix": "ninja://third_party/liburlpattern:liburlpattern_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "media_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "media_unittests",
+        "test_id_prefix": "ninja://media:media_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "midi_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "midi_unittests",
+        "test_id_prefix": "ninja://media/midi:midi_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "mojo_test_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "mojo_test_apk",
+        "test_id_prefix": "ninja://mojo/public/java/system:mojo_test_apk/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "mojo_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "mojo_unittests",
+        "test_id_prefix": "ninja://mojo:mojo_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "monochrome_public_bundle_fake_modules_smoke_test"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "monochrome_public_bundle_fake_modules_smoke_test",
+        "test_id_prefix": "ninja://chrome/android:monochrome_public_bundle_fake_modules_smoke_test/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "monochrome_public_bundle_smoke_test"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "monochrome_public_bundle_smoke_test",
+        "test_id_prefix": "ninja://chrome/android:monochrome_public_bundle_smoke_test/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "monochrome_public_smoke_test"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "monochrome_public_smoke_test",
+        "test_id_prefix": "ninja://chrome/android:monochrome_public_smoke_test/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "net_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
+        },
+        "test": "net_unittests",
+        "test_id_prefix": "ninja://net:net_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "perfetto_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "perfetto_unittests",
+        "test_id_prefix": "ninja://third_party/perfetto:perfetto_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "sandbox_linux_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "sandbox_linux_unittests",
+        "test_id_prefix": "ninja://sandbox/linux:sandbox_linux_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "services_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "services_unittests",
+        "test_id_prefix": "ninja://services:services_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "shell_dialogs_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "shell_dialogs_unittests",
+        "test_id_prefix": "ninja://ui/shell_dialogs:shell_dialogs_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "skia_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "skia_unittests",
+        "test_id_prefix": "ninja://skia:skia_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "sql_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "sql_unittests",
+        "test_id_prefix": "ninja://sql:sql_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "storage_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "storage_unittests",
+        "test_id_prefix": "ninja://storage:storage_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "system_webview_shell_layout_test_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "system_webview_shell_layout_test_apk",
+        "test_id_prefix": "ninja://android_webview/tools/system_webview_shell:system_webview_shell_layout_test_apk/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "ui_android_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "ui_android_unittests",
+        "test_id_prefix": "ninja://ui/android:ui_android_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "ui_base_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "ui_base_unittests",
+        "test_id_prefix": "ninja://ui/base:ui_base_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "ui_touch_selection_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "ui_touch_selection_unittests",
+        "test_id_prefix": "ninja://ui/touch_selection:ui_touch_selection_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "unit_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "unit_tests",
+        "test_id_prefix": "ninja://chrome/test:unit_tests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "url_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "url_unittests",
+        "test_id_prefix": "ninja://url:url_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "viz_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "viz_unittests",
+        "test_id_prefix": "ninja://components/viz:viz_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "vr_android_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "vr_android_unittests",
+        "test_id_prefix": "ninja://chrome/browser/android/vr:vr_android_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "weblayer_browsertests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "weblayer_browsertests",
+        "test_id_prefix": "ninja://weblayer/test:weblayer_browsertests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "weblayer_bundle_test"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "weblayer_bundle_test",
+        "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_bundle_test/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "weblayer_instrumentation_test_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "weblayer_instrumentation_test_apk",
+        "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_instrumentation_test_apk/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "weblayer_private_instrumentation_test_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "weblayer_private_instrumentation_test_apk",
+        "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_private_instrumentation_test_apk/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "weblayer_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "weblayer_unittests",
+        "test_id_prefix": "ninja://weblayer/test:weblayer_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "webview_cts_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/android_webview/tools/cts_archive",
+              "location": "android_webview/tools/cts_archive",
+              "revision": "ai8Ig4HlO0vG6aP_JP2uhyruE2yPzze8PFP1g8Z4_hgC"
+            },
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "webview_cts_tests",
+        "test_id_prefix": "ninja://android_webview/test:webview_cts_tests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "webview_instrumentation_test_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 7
+        },
+        "test": "webview_instrumentation_test_apk",
+        "test_id_prefix": "ninja://android_webview/test:webview_instrumentation_test_apk/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "webview_ui_test_app_test_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webview_ui_test_app_test_apk",
+        "test_id_prefix": "ninja://android_webview/tools/automated_ui_tests:webview_ui_test_app_test_apk/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "wtf_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "wtf_unittests",
+        "test_id_prefix": "ninja://third_party/blink/renderer/platform/wtf:wtf_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_androidS.textpb"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "zlib_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-4",
+              "os": "Ubuntu-16.04|Ubuntu-18.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_androidS",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_S_google_apis_x64",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "optional_dimensions": {
+            "60": [
+              {
+                "caches": "avd_generic_androidS"
+              }
+            ]
+          },
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "zlib_unittests",
+        "test_id_prefix": "ninja://third_party/zlib:zlib_unittests/"
+      }
+    ]
+  },
   "android-pie-arm64-wpt-rel-non-cq": {
     "isolated_scripts": [
       {
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index 3f730e4..7774201 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -180,6 +180,34 @@
       ]
     },
   },
+  # TODO(crbug.com/1225851): Update "S" to the right value once S is released.
+  '12-x64-emulator': {
+    '$mixin_append': {
+      'args': [
+        '--avd-config=../../tools/android/avd/proto/generic_androidS.textpb',
+      ],
+    },
+    'swarming': {
+      # soft affinity so that bots with caches will be picked first
+      'optional_dimensions': {
+        '60': [
+          {
+            'caches': 'avd_generic_androidS',
+          }
+        ],
+      },
+      'named_caches': [
+        {
+          'name': 'avd_generic_androidS',
+          'path': '.android',
+        },
+        {
+          'name': 'system_images_android_S_google_apis_x64',
+          'path': '.emulator_sdk',
+        },
+      ]
+    },
+  },
   'android_r': {
     'swarming': {
       'dimensions': {
@@ -335,6 +363,16 @@
       },
     },
   },
+  'emulator-e2-4-cores': {
+    'swarming': {
+      'dimensions': {
+        'device_os': None,
+        'device_type': None,
+        'pool': 'chromium.tests.avd',
+        'machine_type': 'e2-standard-4',
+      },
+    },
+  },
   'enable_resultdb': {
     'resultdb': {
       'enable': True,
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 4133489c..1649e20 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -5554,6 +5554,20 @@
       'webview_ui_instrumentation_tests',
     ],
 
+    'android_12_emulator_gtests': [
+      'android_monochrome_smoke_tests',
+      'android_smoke_tests',
+      'android_specific_chromium_gtests',  # Already includes gl_gtests.
+      'chromium_gtests',
+      'chromium_gtests_for_devices_with_graphical_output',
+      'linux_flavor_specific_chromium_gtests',
+      'system_webview_shell_instrumentation_tests', # Not an experimental test
+      'weblayer_android_gtests',
+      'weblayer_gtests',
+      'webview_cts_tests_gtest',
+      'webview_ui_instrumentation_tests',
+    ],
+
     # This is the same as 'android_lollipop_marshmallow_gtests'
     # with the addition of 'webview_cts_tests_gtest' and
     # 'webview_ui_instrumentation_tests'
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index ba927d7..ef26aeea 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -1070,6 +1070,19 @@
           'gtest_tests': 'android_11_emulator_gtests',
         }
       },
+      'android-12-x64-fyi-rel': {
+        'mixins': [
+          '12-x64-emulator',
+          'emulator-e2-4-cores',
+          'enable_resultdb',
+          'linux-xenial-or-bionic',
+          'x86-64',
+        ],
+        'os_type': 'android',
+        'test_suites': {
+          'gtest_tests': 'android_12_emulator_gtests',
+        }
+      },
       'android-pie-arm64-wpt-rel-non-cq': {
         'mixins': [
           'enable_resultdb',
diff --git a/third_party/blink/common/tokens/multi_token_unittest.cc b/third_party/blink/common/tokens/multi_token_unittest.cc
index 1305e68..2e2e3535 100644
--- a/third_party/blink/common/tokens/multi_token_unittest.cc
+++ b/third_party/blink/common/tokens/multi_token_unittest.cc
@@ -6,15 +6,15 @@
 
 #include <algorithm>
 
+#include "base/types/token_type.h"
 #include "base/unguessable_token.h"
-#include "base/util/type_safety/token_type.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace blink {
 
-using FooToken = util::TokenType<class FooTokenTag>;
-using BarToken = util::TokenType<class BarTokenTag>;
-using BazToken = util::TokenType<class BazTokenTag>;
+using FooToken = base::TokenType<class FooTokenTag>;
+using BarToken = base::TokenType<class BarTokenTag>;
+using BazToken = base::TokenType<class BazTokenTag>;
 
 // Test MultiTokenVariantCount.
 static_assert(internal::MultiTokenVariantCount<FooToken, BarToken>::kValue == 2,
diff --git a/third_party/blink/public/common/media/watch_time_reporter.h b/third_party/blink/public/common/media/watch_time_reporter.h
index f48d94c..08cdc7c 100644
--- a/third_party/blink/public/common/media/watch_time_reporter.h
+++ b/third_party/blink/public/common/media/watch_time_reporter.h
@@ -25,10 +25,6 @@
 #include "ui/gfx/geometry/size.h"
 #include "url/origin.h"
 
-namespace media {
-class WatchTimeReporterTest;
-}
-
 namespace blink {
 
 // Class for monitoring and reporting watch time in response to various state
@@ -161,7 +157,7 @@
   void OnDurationChanged(base::TimeDelta duration);
 
  private:
-  friend class media::WatchTimeReporterTest;
+  friend class WatchTimeReporterTest;
 
   // Internal constructor for marking background status.
   WatchTimeReporter(media::mojom::PlaybackPropertiesPtr properties,
diff --git a/third_party/blink/public/common/tokens/README.md b/third_party/blink/public/common/tokens/README.md
index c8bd024..dbc55df 100644
--- a/third_party/blink/public/common/tokens/README.md
+++ b/third_party/blink/public/common/tokens/README.md
@@ -3,7 +3,7 @@
 ## Overview
 
 This directory contains strongly-typed wrappers (using
-[`util::TokenType<...>`](/base/util/type_safety/token_type.h)) of
+[`base::TokenType<...>`](/base/types/token_type.h)) of
 [`base::UnguessableToken`](/base/unguessable_token.h)
 for tokens that are commonly passed between browsers and renderers. The strong
 typing is to prevent type confusion as these tokens are passed around. To support
diff --git a/third_party/blink/public/common/tokens/multi_token.h b/third_party/blink/public/common/tokens/multi_token.h
index 7b00b328..a776baa 100644
--- a/third_party/blink/public/common/tokens/multi_token.h
+++ b/third_party/blink/public/common/tokens/multi_token.h
@@ -18,7 +18,7 @@
 namespace blink {
 
 // Defines MultiToken, which is effectively a variant over 2 or more
-// instances of util::TokenType.
+// instances of base::TokenType.
 //
 // A MultiToken<..> emulates a token like interface. When default constructed
 // it will construct itself as an instance of |TokenVariant0|. Additionally it
@@ -34,7 +34,7 @@
 //
 // A variant must have at least 2 valid input types, but can have arbitrarily
 // many. They must all be distinct, and they must all be instances of
-// util::TokenType.
+// base::TokenType.
 template <typename TokenVariant0,
           typename TokenVariant1,
           typename... TokenVariants>
diff --git a/third_party/blink/public/common/tokens/multi_token_internal.h b/third_party/blink/public/common/tokens/multi_token_internal.h
index 8c2f7f37..9a3dcec 100644
--- a/third_party/blink/public/common/tokens/multi_token_internal.h
+++ b/third_party/blink/public/common/tokens/multi_token_internal.h
@@ -12,8 +12,8 @@
 #include <cstring>
 #include <type_traits>
 
+#include "base/types/token_type.h"
 #include "base/unguessable_token.h"
-#include "base/util/type_safety/token_type.h"
 
 namespace blink {
 
@@ -46,7 +46,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // MultiTokenVariantIsTokenType
 //
-// Ensures if a QueryType is a a util::TokenType<>.
+// Ensures if a QueryType is a a base::TokenType<>.
 
 // Default case.
 template <typename QueryType>
@@ -54,9 +54,9 @@
   static constexpr bool kValue = false;
 };
 
-// Specialization for util::TokenType<>.
+// Specialization for base::TokenType<>.
 template <typename TokenTypeTag>
-struct MultiTokenVariantIsTokenType<::util::TokenType<TokenTypeTag>> {
+struct MultiTokenVariantIsTokenType<::base::TokenType<TokenTypeTag>> {
   static constexpr bool kValue = true;
 
   // We expect an identical layout, which allows us to reinterpret_cast between
@@ -64,19 +64,19 @@
   // we can check whether or not the compiler is sane (and if the behaviour is
   // safe) at compile-time.
   static_assert(
-      sizeof(::util::TokenType<TokenTypeTag>) ==
+      sizeof(::base::TokenType<TokenTypeTag>) ==
           sizeof(::base::UnguessableToken),
-      "util::TokenType must have the same sizeof as base::UnguessableToken");
+      "base::TokenType must have the same sizeof as base::UnguessableToken");
   static_assert(
-      alignof(::util::TokenType<TokenTypeTag>) ==
+      alignof(::base::TokenType<TokenTypeTag>) ==
           alignof(::base::UnguessableToken),
-      "util::TokenType must have the same alignof as base::UnguessableToken");
+      "base::TokenType must have the same alignof as base::UnguessableToken");
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 // MultiTokenAllVariantsAreTokenType
 //
-// Ensures that all variants are of type util::TokenType.
+// Ensures that all variants are of type base::TokenType.
 
 template <typename... VariantTypes>
 struct MultiTokenAllVariantsAreTokenType;
@@ -187,11 +187,11 @@
   using AnyRepeated = internal::MultiTokenAnyTypeRepeated<TokenVariants...>;
   static_assert(!AnyRepeated::kValue, "input types must not be repeated");
 
-  // Ensures that all variants are instances of util::TokenType.
+  // Ensures that all variants are instances of base::TokenType.
   using AllVariantsAreTokenType =
       internal::MultiTokenAllVariantsAreTokenType<TokenVariants...>;
   static_assert(AllVariantsAreTokenType::kValue,
-                "input types must be instances of util::TokenType");
+                "input types must be instances of base::TokenType");
 
   // Counts the number of variants.
   using VariantCount = internal::MultiTokenVariantCount<TokenVariants...>;
diff --git a/third_party/blink/public/common/tokens/token_mojom_traits_helper.h b/third_party/blink/public/common/tokens/token_mojom_traits_helper.h
index 50b6fc9..0f5ee10a3 100644
--- a/third_party/blink/public/common/tokens/token_mojom_traits_helper.h
+++ b/third_party/blink/public/common/tokens/token_mojom_traits_helper.h
@@ -11,7 +11,7 @@
 
 // Defines Mojo StructTraits that convert between the given |MojomDataViewType|
 // and the given |TokenType|. It is assumed that TokenType is an instance of
-// util::TokenType<...> and that MojomDataViewType is a simple mojom struct
+// base::TokenType<...> and that MojomDataViewType is a simple mojom struct
 // containing only a "base.mojom.UnguessableToken value" field.
 template <typename MojomDataViewType, typename TokenType>
 struct TokenMojomTraitsHelper {
diff --git a/third_party/blink/public/common/tokens/tokens.h b/third_party/blink/public/common/tokens/tokens.h
index fad917f..d96a7eb 100644
--- a/third_party/blink/public/common/tokens/tokens.h
+++ b/third_party/blink/public/common/tokens/tokens.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_TOKENS_H_
 #define THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_TOKENS_H_
 
-#include "base/util/type_safety/token_type.h"
+#include "base/types/token_type.h"
 #include "third_party/blink/public/common/tokens/multi_token.h"
 
 namespace blink {
@@ -26,14 +26,14 @@
 // Uniquely identifies a blink::LocalFrame / blink::WebLocalFrame /
 // content::RenderFrame in a renderer process, and its content::RenderFrameHost
 // counterpart in the browser.
-using LocalFrameToken = util::TokenType<class LocalFrameTokenTypeMarker>;
+using LocalFrameToken = base::TokenType<class LocalFrameTokenTypeMarker>;
 
 // Uniquely identifies a blink::RemoteFrame / blink::WebRemoteFrame /
 // content::RenderFrameProxy in a renderer process, and its
 // content::RenderFrameProxyHost counterpart in the browser. There can be
 // multiple RemoteFrames corresponding to a single LocalFrame, and each token
 // will be distinct.
-using RemoteFrameToken = util::TokenType<class RemoteFrameTokenTypeMarker>;
+using RemoteFrameToken = base::TokenType<class RemoteFrameTokenTypeMarker>;
 
 // Can represent either type of FrameToken.
 using FrameToken = MultiToken<LocalFrameToken, RemoteFrameToken>;
@@ -44,15 +44,15 @@
 // Identifies a blink::DedicatedWorkerGlobalScope in the renderer and a
 // content::DedicatedWorkerHost in the browser.
 using DedicatedWorkerToken =
-    util::TokenType<class DedicatedWorkerTokenTypeMarker>;
+    base::TokenType<class DedicatedWorkerTokenTypeMarker>;
 
 // Identifies a blink::ServiceWorkerGlobalScope in the renderer and a
 // content::ServiceWorkerVersion in the browser.
-using ServiceWorkerToken = util::TokenType<class ServiceWorkerTokenTypeMarker>;
+using ServiceWorkerToken = base::TokenType<class ServiceWorkerTokenTypeMarker>;
 
 // Identifies a blink::SharedWorkerGlobalScope in the renderer and a
 // content::SharedWorkerHost in the browser.
-using SharedWorkerToken = util::TokenType<class SharedWorkerTokenTypeMarker>;
+using SharedWorkerToken = base::TokenType<class SharedWorkerTokenTypeMarker>;
 
 // Can represent any type of WorkerToken.
 using WorkerToken =
@@ -63,16 +63,16 @@
 
 // Identifies an animation worklet.
 using AnimationWorkletToken =
-    util::TokenType<class AnimationWorkletTokenTypeMarker>;
+    base::TokenType<class AnimationWorkletTokenTypeMarker>;
 
 // Identifies an audio worklet.
-using AudioWorkletToken = util::TokenType<class AudioWorkletTokenTypeMarker>;
+using AudioWorkletToken = base::TokenType<class AudioWorkletTokenTypeMarker>;
 
 // Identifies a layout worklet.
-using LayoutWorkletToken = util::TokenType<class LayoutWorkletTokenTypeMarker>;
+using LayoutWorkletToken = base::TokenType<class LayoutWorkletTokenTypeMarker>;
 
 // Identifies a paint worklet.
-using PaintWorkletToken = util::TokenType<class PaintWorkletTokenTypeMarker>;
+using PaintWorkletToken = base::TokenType<class PaintWorkletTokenTypeMarker>;
 
 // Can represent any type of WorkletToken.
 using WorkletToken = MultiToken<AnimationWorkletToken,
@@ -102,10 +102,10 @@
 
 // Identifies a blink::PortalContents / blink::HTMLPortalElement in the
 // renderer process, and a content::Portal in the browser process.
-using PortalToken = util::TokenType<class PortalTokenTypeMarker>;
+using PortalToken = base::TokenType<class PortalTokenTypeMarker>;
 
 // Identifies a v8::Context / blink::ScriptState.
-using V8ContextToken = util::TokenType<class V8ContextTokenTypeMarker>;
+using V8ContextToken = base::TokenType<class V8ContextTokenTypeMarker>;
 
 }  // namespace blink
 
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 350d0cf7..494170a 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -3265,6 +3265,11 @@
   kSanitizerAPIElementSetSanitized = 3950,
   kTextShadowInHighlightPseudo = 3951,
   kTextShadowNotNoneInHighlightPseudo = 3952,
+  kSameSiteNoneRequired = 3953,
+  kSameSiteNoneIncludedBySamePartyTopResource = 3954,
+  kSameSiteNoneIncludedBySamePartyAncestors = 3955,
+  kSameSiteNoneIncludedBySameSiteLax = 3956,
+  kSameSiteNoneIncludedBySameSiteStrict = 3957,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/media/buffered_data_source_host_impl.h b/third_party/blink/public/platform/media/buffered_data_source_host_impl.h
index 34b042da..6ba7be2e 100644
--- a/third_party/blink/public/platform/media/buffered_data_source_host_impl.h
+++ b/third_party/blink/public/platform/media/buffered_data_source_host_impl.h
@@ -16,7 +16,7 @@
 #include "third_party/blink/public/platform/media/interval_map.h"
 #include "third_party/blink/public/platform/web_common.h"
 
-namespace media {
+namespace blink {
 
 // Interface for testing purposes.
 class BLINK_PLATFORM_EXPORT BufferedDataSourceHost {
@@ -97,6 +97,6 @@
                            CanPlayThroughSmallAdvances);
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MEDIA_BUFFERED_DATA_SOURCE_HOST_IMPL_H_
diff --git a/third_party/blink/public/platform/media/interval_map.h b/third_party/blink/public/platform/media/interval_map.h
index 436fa09..d25edff 100644
--- a/third_party/blink/public/platform/media/interval_map.h
+++ b/third_party/blink/public/platform/media/interval_map.h
@@ -12,7 +12,7 @@
 #include "base/check.h"
 #include "third_party/blink/public/platform/web_common.h"
 
-namespace media {
+namespace blink {
 
 // An IntervalMap<KeyType, ValueType> maps every value of KeyType to
 // a ValueType, and incrementing, decrementing and setting ranges of values
@@ -285,6 +285,6 @@
   MapType map_;
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MEDIA_INTERVAL_MAP_H_
diff --git a/third_party/blink/public/platform/media/key_system_config_selector.h b/third_party/blink/public/platform/media/key_system_config_selector.h
index 1318f10..e4e14a8a 100644
--- a/third_party/blink/public/platform/media/key_system_config_selector.h
+++ b/third_party/blink/public/platform/media/key_system_config_selector.h
@@ -19,18 +19,15 @@
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 
-namespace blink {
-
-struct WebMediaKeySystemConfiguration;
-class WebString;
-
-}  // namespace blink
-
 namespace media {
-
-struct CdmConfig;
 class KeySystems;
 class MediaPermission;
+struct CdmConfig;
+}  // namespace media
+
+namespace blink {
+class WebString;
+struct WebMediaKeySystemConfiguration;
 
 class BLINK_PLATFORM_EXPORT KeySystemConfigSelector {
  public:
@@ -78,10 +75,8 @@
 
   // Callback for the result of `SelectConfig()`. The returned configs must be
   // non-null iff `status` is `kSupported`.
-  using SelectConfigCB =
-      base::OnceCallback<void(Status status,
-                              blink::WebMediaKeySystemConfiguration*,
-                              media::CdmConfig*)>;
+  using SelectConfigCB = base::OnceCallback<
+      void(Status status, WebMediaKeySystemConfiguration*, media::CdmConfig*)>;
 
   void SelectConfig(
       const blink::WebString& key_system,
@@ -156,6 +151,6 @@
   base::WeakPtrFactory<KeySystemConfigSelector> weak_factory_{this};
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MEDIA_KEY_SYSTEM_CONFIG_SELECTOR_H_
diff --git a/third_party/blink/public/platform/media/learning_experiment_helper.h b/third_party/blink/public/platform/media/learning_experiment_helper.h
index f960484..890380a 100644
--- a/third_party/blink/public/platform/media/learning_experiment_helper.h
+++ b/third_party/blink/public/platform/media/learning_experiment_helper.h
@@ -13,7 +13,7 @@
 #include "media/learning/common/learning_task_controller.h"
 #include "third_party/blink/public/platform/web_common.h"
 
-namespace media {
+namespace blink {
 
 // Helper for adding a learning experiment to existing code.
 class BLINK_PLATFORM_EXPORT LearningExperimentHelper {
@@ -47,6 +47,6 @@
   base::UnguessableToken observation_id_;
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MEDIA_LEARNING_EXPERIMENT_HELPER_H_
diff --git a/third_party/blink/public/platform/media/lru.h b/third_party/blink/public/platform/media/lru.h
index d51c608..d6885938 100644
--- a/third_party/blink/public/platform/media/lru.h
+++ b/third_party/blink/public/platform/media/lru.h
@@ -13,7 +13,7 @@
 #include "base/check.h"
 #include "third_party/blink/public/platform/web_common.h"
 
-namespace media {
+namespace blink {
 
 // Simple LRU (least recently used) class.
 // Keeps track of a set of data and lets you get the least recently used
@@ -91,6 +91,6 @@
   std::unordered_map<T, typename std::list<T>::iterator> pos_;
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MEDIA_LRU_H_
diff --git a/third_party/blink/public/platform/media/multi_buffer.h b/third_party/blink/public/platform/media/multi_buffer.h
index 24adba3..b6331d4b 100644
--- a/third_party/blink/public/platform/media/multi_buffer.h
+++ b/third_party/blink/public/platform/media/multi_buffer.h
@@ -27,7 +27,7 @@
 #include "third_party/blink/public/platform/media/lru.h"
 #include "third_party/blink/public/platform/web_common.h"
 
-namespace media {
+namespace blink {
 
 // Used to identify a block of data in the multibuffer.
 // Our blocks are 32kb (1 << 15), so our maximum cacheable file size
@@ -39,20 +39,20 @@
 // multibuffers.
 typedef std::pair<MultiBuffer*, MultiBufferBlockId> MultiBufferGlobalBlockId;
 
-}  // namespace media
+}  // namespace blink
 
 namespace std {
 
 template <>
-struct hash<media::MultiBufferGlobalBlockId> {
-  std::size_t operator()(const media::MultiBufferGlobalBlockId& key) const {
+struct hash<blink::MultiBufferGlobalBlockId> {
+  std::size_t operator()(const blink::MultiBufferGlobalBlockId& key) const {
     return base::HashInts(reinterpret_cast<uintptr_t>(key.first), key.second);
   }
 };
 
 }  // namespace std
 
-namespace media {
+namespace blink {
 
 // Freeing a lot of blocks can be expensive, to keep thing
 // flowing smoothly we only free a maximum of |kMaxFreesPerAdd|
@@ -123,7 +123,7 @@
     // returns true. Last block might be of a smaller size
     // and after the last block we will get an end-of-stream
     // DataBuffer.
-    virtual scoped_refptr<DataBuffer> Read() = 0;
+    virtual scoped_refptr<media::DataBuffer> Read() = 0;
 
     // Ask the data provider to stop giving us data.
     // It's ok if the effect is not immediate.
@@ -210,7 +210,7 @@
   // Block numbers can be calculated from byte positions as:
   // block_num = byte_pos >> block_size_shift
   typedef MultiBufferBlockId BlockId;
-  typedef std::unordered_map<BlockId, scoped_refptr<DataBuffer>> DataMap;
+  typedef std::unordered_map<BlockId, scoped_refptr<media::DataBuffer>> DataMap;
 
   // Registers a reader at the given position.
   // If the cache does not already contain |pos|, it will activate
@@ -261,9 +261,10 @@
 
   // Returns a continous (but possibly empty) list of blocks starting at
   // |from| up to, but not including |to|. This function is thread safe.
-  void GetBlocksThreadsafe(const BlockId& from,
-                           const BlockId& to,
-                           std::vector<scoped_refptr<DataBuffer>>* output);
+  void GetBlocksThreadsafe(
+      const BlockId& from,
+      const BlockId& to,
+      std::vector<scoped_refptr<media::DataBuffer>>* output);
 
   // Increment max cache size by |size| (counted in blocks).
   void IncrementMaxSize(int32_t size);
@@ -382,6 +383,6 @@
   IntervalMap<BlockId, int32_t> present_;
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MEDIA_MULTI_BUFFER_H_
diff --git a/third_party/blink/public/platform/media/multi_buffer_data_source.h b/third_party/blink/public/platform/media/multi_buffer_data_source.h
index e362c710..2e02060 100644
--- a/third_party/blink/public/platform/media/multi_buffer_data_source.h
+++ b/third_party/blink/public/platform/media/multi_buffer_data_source.h
@@ -26,8 +26,11 @@
 }
 
 namespace media {
-class BufferedDataSourceHost;
 class MediaLog;
+}
+
+namespace blink {
+class BufferedDataSourceHost;
 class MultiBufferReader;
 
 // A data source capable of loading URLs and buffering the data using an
@@ -291,6 +294,6 @@
   base::WeakPtrFactory<MultiBufferDataSource> weak_factory_{this};
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MEDIA_MULTI_BUFFER_DATA_SOURCE_H_
diff --git a/third_party/blink/public/platform/media/power_status_helper.h b/third_party/blink/public/platform/media/power_status_helper.h
index 82cd911..6467d5a 100644
--- a/third_party/blink/public/platform/media/power_status_helper.h
+++ b/third_party/blink/public/platform/media/power_status_helper.h
@@ -18,6 +18,9 @@
 
 namespace media {
 struct PipelineMetadata;
+}
+
+namespace blink {
 
 // Class to monitor for power events during playback and record them to UMA/UKM.
 class BLINK_PLATFORM_EXPORT PowerStatusHelper {
@@ -143,6 +146,6 @@
   SEQUENCE_CHECKER(sequence_checker_);
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MEDIA_POWER_STATUS_HELPER_H_
diff --git a/third_party/blink/public/platform/media/remote_playback_client_wrapper_impl.h b/third_party/blink/public/platform/media/remote_playback_client_wrapper_impl.h
index c6f1f4da1b..ea7b036 100644
--- a/third_party/blink/public/platform/media/remote_playback_client_wrapper_impl.h
+++ b/third_party/blink/public/platform/media/remote_playback_client_wrapper_impl.h
@@ -13,14 +13,11 @@
 namespace blink {
 class WebMediaPlayerClient;
 class WebRemotePlaybackClient;
-}  // namespace blink
-
-namespace media {
 
 // Wraps a WebRemotePlaybackClient to expose only the methods used by the
 // FlingingRendererClientFactory. This avoids dependencies on the blink layer.
 class BLINK_PLATFORM_EXPORT RemotePlaybackClientWrapperImpl
-    : public RemotePlaybackClientWrapper {
+    : public media::RemotePlaybackClientWrapper {
  public:
   explicit RemotePlaybackClientWrapperImpl(blink::WebMediaPlayerClient* client);
   ~RemotePlaybackClientWrapperImpl() override;
@@ -31,6 +28,6 @@
   blink::WebRemotePlaybackClient* remote_playback_client_;
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MEDIA_REMOTE_PLAYBACK_CLIENT_WRAPPER_IMPL_H_
diff --git a/third_party/blink/public/platform/media/resource_fetch_context.h b/third_party/blink/public/platform/media/resource_fetch_context.h
index 80e63a4..ae83ba0 100644
--- a/third_party/blink/public/platform/media/resource_fetch_context.h
+++ b/third_party/blink/public/platform/media/resource_fetch_context.h
@@ -12,7 +12,7 @@
 #include "third_party/blink/public/web/web_associated_url_loader.h"
 #include "third_party/blink/public/web/web_associated_url_loader_options.h"
 
-namespace media {
+namespace blink {
 
 class BLINK_PLATFORM_EXPORT ResourceFetchContext {
  public:
@@ -22,6 +22,6 @@
       const blink::WebAssociatedURLLoaderOptions& options) = 0;
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MEDIA_RESOURCE_FETCH_CONTEXT_H_
diff --git a/third_party/blink/public/platform/media/smoothness_helper.h b/third_party/blink/public/platform/media/smoothness_helper.h
index e142a21..caf2d33 100644
--- a/third_party/blink/public/platform/media/smoothness_helper.h
+++ b/third_party/blink/public/platform/media/smoothness_helper.h
@@ -13,10 +13,12 @@
 #include "third_party/blink/public/platform/web_common.h"
 
 namespace media {
-
 namespace learning {
 class LearningTaskController;
 }
+}  // namespace media
+
+namespace blink {
 
 // Helper class to construct learning observations about the smoothness of a
 // video playback.  Currently measures the worst-case frame drop ratio observed
@@ -62,6 +64,6 @@
   media::learning::FeatureVector features_;
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MEDIA_SMOOTHNESS_HELPER_H_
diff --git a/third_party/blink/public/platform/media/url_index.h b/third_party/blink/public/platform/media/url_index.h
index dc38bd1..e777caf 100644
--- a/third_party/blink/public/platform/media/url_index.h
+++ b/third_party/blink/public/platform/media/url_index.h
@@ -27,7 +27,7 @@
 class SingleThreadTaskRunner;
 }
 
-namespace media {
+namespace blink {
 
 const int64_t kPositionNotSpecified = -1;
 
@@ -308,5 +308,6 @@
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 };
 
-}  // namespace media
+}  // namespace blink
+
 #endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MEDIA_URL_INDEX_H_
diff --git a/third_party/blink/public/platform/media/video_frame_compositor.h b/third_party/blink/public/platform/media/video_frame_compositor.h
index ee4044d20..5dab5d34 100644
--- a/third_party/blink/public/platform/media/video_frame_compositor.h
+++ b/third_party/blink/public/platform/media/video_frame_compositor.h
@@ -27,12 +27,15 @@
 class WaitableEvent;
 }
 
+namespace media {
+class VideoFrame;
+}
+
 namespace viz {
 class SurfaceId;
 }
 
-namespace media {
-class VideoFrame;
+namespace blink {
 
 // VideoFrameCompositor acts as a bridge between the media and cc layers for
 // rendering video frames. I.e. a media::VideoRenderer will talk to this class
@@ -291,6 +294,6 @@
   base::WeakPtrFactory<VideoFrameCompositor> weak_ptr_factory_{this};
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MEDIA_VIDEO_FRAME_COMPOSITOR_H_
diff --git a/third_party/blink/public/platform/media/web_encrypted_media_client_impl.h b/third_party/blink/public/platform/media/web_encrypted_media_client_impl.h
index 7fa24d9..23d8184 100644
--- a/third_party/blink/public/platform/media/web_encrypted_media_client_impl.h
+++ b/third_party/blink/public/platform/media/web_encrypted_media_client_impl.h
@@ -16,19 +16,16 @@
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_encrypted_media_client.h"
 
-namespace blink {
-
-class WebContentDecryptionModuleResult;
-struct WebMediaKeySystemConfiguration;
-class WebSecurityOrigin;
-
-}  // namespace blink
-
 namespace media {
-
-struct CdmConfig;
 class CdmFactory;
 class MediaPermission;
+struct CdmConfig;
+}  // namespace media
+
+namespace blink {
+class WebContentDecryptionModuleResult;
+class WebSecurityOrigin;
+struct WebMediaKeySystemConfiguration;
 
 class BLINK_PLATFORM_EXPORT WebEncryptedMediaClientImpl
     : public blink::WebEncryptedMediaClient {
@@ -80,6 +77,6 @@
   base::WeakPtrFactory<WebEncryptedMediaClientImpl> weak_factory_{this};
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MEDIA_WEB_ENCRYPTED_MEDIA_CLIENT_IMPL_H_
diff --git a/third_party/blink/public/platform/media/web_media_player_impl.h b/third_party/blink/public/platform/media/web_media_player_impl.h
index 2d4604c0..9d5ba3159 100644
--- a/third_party/blink/public/platform/media/web_media_player_impl.h
+++ b/third_party/blink/public/platform/media/web_media_player_impl.h
@@ -51,14 +51,6 @@
 #include "third_party/blink/public/web/modules/media/webmediaplayer_util.h"
 #include "url/gurl.h"
 
-namespace blink {
-class WebAudioSourceProviderImpl;
-class WebLocalFrame;
-class WebMediaPlayerClient;
-class WebMediaPlayerEncryptedMediaClient;
-class WatchTimeReporter;
-}  // namespace blink
-
 namespace base {
 class SingleThreadTaskRunner;
 class TaskRunner;
@@ -76,20 +68,28 @@
 class LearningTaskController;
 }
 
+namespace media {
+class CdmContextRef;
+class ChunkDemuxer;
+class MediaLog;
+class MemoryDumpProviderProxy;
+class PipelineController;
+}  // namespace media
+
 namespace viz {
 class RasterContextProvider;
 }
 
-namespace media {
-class CdmContextRef;
-class ChunkDemuxer;
-class VideoDecodeStatsReporter;
-class MediaLog;
-class MemoryDumpProviderProxy;
-class PipelineController;
+namespace blink {
 class PowerStatusHelper;
 class UrlIndex;
+class VideoDecodeStatsReporter;
 class VideoFrameCompositor;
+class WatchTimeReporter;
+class WebAudioSourceProviderImpl;
+class WebLocalFrame;
+class WebMediaPlayerClient;
+class WebMediaPlayerEncryptedMediaClient;
 
 // The canonical implementation of blink::WebMediaPlayer that's backed by
 // Pipeline. Handles normal resource loading, Media Source, and
@@ -1046,6 +1046,6 @@
   base::WeakPtrFactory<WebMediaPlayerImpl> weak_factory_{this};
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MEDIA_WEB_MEDIA_PLAYER_IMPL_H_
diff --git a/third_party/blink/public/platform/media/web_media_player_params.h b/third_party/blink/public/platform/media/web_media_player_params.h
index ff45034..09d246d 100644
--- a/third_party/blink/public/platform/media/web_media_player_params.h
+++ b/third_party/blink/public/platform/media/web_media_player_params.h
@@ -29,22 +29,21 @@
 class TaskRunner;
 }
 
+namespace media {
+class Demuxer;
+class SwitchableAudioRendererSink;
+}  // namespace media
+
 namespace blink {
 class WebContentDecryptionModule;
 class WebSurfaceLayerBridge;
 class WebSurfaceLayerBridgeObserver;
-}  // namespace blink
-
-namespace media {
 
 using CreateSurfaceLayerBridgeCB =
     base::OnceCallback<std::unique_ptr<blink::WebSurfaceLayerBridge>(
         blink::WebSurfaceLayerBridgeObserver*,
         cc::UpdateSubmissionStateCB)>;
 
-class Demuxer;
-class SwitchableAudioRendererSink;
-
 // Holds parameters for constructing WebMediaPlayerImpl without having
 // to plumb arguments through various abstraction layers.
 class BLINK_PLATFORM_EXPORT WebMediaPlayerParams {
@@ -214,6 +213,6 @@
   std::unique_ptr<PowerStatusHelper> power_status_helper_;
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MEDIA_WEB_MEDIA_PLAYER_PARAMS_H_
diff --git a/third_party/blink/renderer/core/css/media_values_cached.cc b/third_party/blink/renderer/core/css/media_values_cached.cc
index 4abee10..3eeb7793 100644
--- a/third_party/blink/renderer/core/css/media_values_cached.cc
+++ b/third_party/blink/renderer/core/css/media_values_cached.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/public/common/css/navigation_controls.h"
 #include "third_party/blink/public/mojom/css/preferred_color_scheme.mojom-blink.h"
 #include "third_party/blink/public/mojom/css/preferred_contrast.mojom-blink.h"
+#include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom-blink.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/media_values.h"
 #include "third_party/blink/renderer/core/dom/document.h"
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_observer.h b/third_party/blink/renderer/core/loader/resource/image_resource_observer.h
index 51f9be8..c2e31c6 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_observer.h
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_observer.h
@@ -23,7 +23,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_RESOURCE_IMAGE_RESOURCE_OBSERVER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_RESOURCE_IMAGE_RESOURCE_OBSERVER_H_
 
-#include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom-blink.h"
+#include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/style/style_image.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_priority.h"
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index a92293f..f748b7c 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -1135,6 +1135,21 @@
   return nullptr;
 }
 
+const PaintLayer* PaintLayer::EnclosingCompositedScrollingLayerUnderPagination(
+    IncludeSelfOrNot include_self_or_not) const {
+  DCHECK(RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
+  const auto* start_layer =
+      include_self_or_not == kIncludeSelf ? this : CompositingContainer();
+  for (const auto* curr = start_layer; curr && curr->EnclosingPaginationLayer();
+       curr = curr->CompositingContainer()) {
+    if (const auto* scrollable_area = curr->GetScrollableArea()) {
+      if (scrollable_area->NeedsCompositedScrolling())
+        return curr;
+    }
+  }
+  return nullptr;
+}
+
 void PaintLayer::SetNeedsCompositingInputsUpdate(bool mark_ancestor_flags) {
   SetNeedsCompositingInputsUpdateInternal();
 
@@ -1739,8 +1754,12 @@
     const PaintLayer* compositing_layer) const {
   if (!EnclosingPaginationLayer())
     return false;
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
-    return true;
+  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+    // We should not fragment composited scrolling layers and descendants, which
+    // is not only to render the scroller correctly, but also to prevent
+    // multiple cc::Layers with the same scrolling element id.
+    return !EnclosingCompositedScrollingLayerUnderPagination(kIncludeSelf);
+  }
   if (Transform() &&
       !PaintsWithDirectReasonIntoOwnBacking(kGlobalPaintNormalPhase))
     return true;
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h
index 1b60e2d..150cc19a 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.h
+++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -408,6 +408,10 @@
 
   PaintLayer* EnclosingDirectlyCompositableLayer(IncludeSelfOrNot) const;
 
+  // For CompositeAfterPaint, but not for LayoutNGBlockFragmentation.
+  const PaintLayer* EnclosingCompositedScrollingLayerUnderPagination(
+      IncludeSelfOrNot) const;
+
   // https://crbug.com/751768, this function can return nullptr sometimes.
   // Always check the result before using it, don't just DCHECK.
   PaintLayer* EnclosingLayerForPaintInvalidationCrossingFrameBoundaries() const;
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 95d27e3..3d0cc4920 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -2963,12 +2963,15 @@
 
   // Column-span:all skips pagination container in the tree hierarchy, so it
   // should also skip any fragment clip created by the skipped pagination
-  // container. We also need to skip fragment clip if the object is a paint
-  // invalidation container which doesn't allow fragmentation.
-  bool skip_fragment_clip_for_composited_layer =
-      !RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
-      object_.CanBeCompositedForDirectReasons() &&
-      To<LayoutBoxModelObject>(object_).Layer()->EnclosingPaginationLayer();
+  // container. We also need to skip fragment clip if the layer doesn't allow
+  // fragmentation.
+  bool skip_fragment_clip_for_composited_layer = false;
+  if (object_.HasLayer()) {
+    const auto* layer = To<LayoutBoxModelObject>(object_).Layer();
+    skip_fragment_clip_for_composited_layer =
+        layer->EnclosingPaginationLayer() &&
+        !layer->ShouldFragmentCompositedBounds();
+  }
   if (!skip_fragment_clip_for_composited_layer && !object_.IsColumnSpanAll())
     return;
 
@@ -3011,8 +3014,6 @@
 
 void PaintPropertyTreeBuilder::UpdateCompositedLayerPaginationOffset() {
   DCHECK(!IsInNGFragmentTraversal());
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
-    return;
 
   const auto* enclosing_pagination_layer =
       context_.painting_layer->EnclosingPaginationLayer();
@@ -3026,13 +3027,28 @@
   DCHECK(!context_.painting_layer->ShouldFragmentCompositedBounds());
   FragmentData& first_fragment =
       object_.GetMutableForPainting().FirstFragment();
-  bool can_be_directly_composited = object_.CanBeCompositedForDirectReasons();
-  const auto* parent_directly_composited_layer =
-      context_.painting_layer->EnclosingDirectlyCompositableLayer(
-          can_be_directly_composited ? kExcludeSelf : kIncludeSelf);
-  if (can_be_directly_composited &&
-      (!parent_directly_composited_layer ||
-       !parent_directly_composited_layer->EnclosingPaginationLayer())) {
+  bool may_use_self_pagination_offset = false;
+  const PaintLayer* parent_pagination_offset_layer = nullptr;
+  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+    parent_pagination_offset_layer =
+        context_.painting_layer
+            ->EnclosingCompositedScrollingLayerUnderPagination(kIncludeSelf);
+    if (parent_pagination_offset_layer->GetLayoutObject() == object_) {
+      may_use_self_pagination_offset = true;
+      parent_pagination_offset_layer =
+          parent_pagination_offset_layer
+              ->EnclosingCompositedScrollingLayerUnderPagination(kExcludeSelf);
+    }
+  } else {
+    may_use_self_pagination_offset = object_.CanBeCompositedForDirectReasons();
+    parent_pagination_offset_layer =
+        context_.painting_layer->EnclosingDirectlyCompositableLayer(
+            may_use_self_pagination_offset ? kExcludeSelf : kIncludeSelf);
+  }
+
+  if (may_use_self_pagination_offset &&
+      (!parent_pagination_offset_layer ||
+       !parent_pagination_offset_layer->EnclosingPaginationLayer())) {
     // |object_| establishes the top level composited layer under the
     // pagination layer.
     FragmentainerIterator iterator(
@@ -3045,10 +3061,10 @@
       first_fragment.SetLogicalTopInFlowThread(
           iterator.FragmentainerLogicalTopInFlowThread());
     }
-  } else if (parent_directly_composited_layer) {
+  } else if (parent_pagination_offset_layer) {
     // All objects under the composited layer use the same pagination offset.
     const auto& fragment =
-        parent_directly_composited_layer->GetLayoutObject().FirstFragment();
+        parent_pagination_offset_layer->GetLayoutObject().FirstFragment();
     first_fragment.SetLegacyPaginationOffset(fragment.LegacyPaginationOffset());
     first_fragment.SetLogicalTopInFlowThread(fragment.LogicalTopInFlowThread());
   }
diff --git a/third_party/blink/renderer/core/svg/animation/smil_time_container.h b/third_party/blink/renderer/core/svg/animation/smil_time_container.h
index badbc137..9c37ebf 100644
--- a/third_party/blink/renderer/core/svg/animation/smil_time_container.h
+++ b/third_party/blink/renderer/core/svg/animation/smil_time_container.h
@@ -28,7 +28,7 @@
 
 #include "base/dcheck_is_on.h"
 #include "base/time/time.h"
-#include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom-blink.h"
+#include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/svg/animation/priority_queue.h"
 #include "third_party/blink/renderer/core/svg/animation/smil_time.h"
diff --git a/third_party/blink/renderer/modules/accessibility/ax_menu_list_popup.cc b/third_party/blink/renderer/modules/accessibility/ax_menu_list_popup.cc
index 10cd1b5..cc959dde 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_menu_list_popup.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_menu_list_popup.cc
@@ -152,14 +152,14 @@
   if (old_index != option_index && old_index >= 0 &&
       old_index < static_cast<int>(children_.size())) {
     AXObject* previous_child = children_[old_index].Get();
-    cache.MarkAXObjectDirtyWithCleanLayout(previous_child, false);
+    cache.MarkAXObjectDirtyWithCleanLayout(previous_child);
   }
 
   if (option_index >= 0 && option_index < static_cast<int>(children_.size())) {
     AXObject* child = children_[option_index].Get();
     cache.PostNotification(this, ax::mojom::Event::kChildrenChanged);
     cache.PostNotification(this, ax::mojom::Event::kActiveDescendantChanged);
-    cache.MarkAXObjectDirtyWithCleanLayout(child, false);
+    cache.MarkAXObjectDirtyWithCleanLayout(child);
   }
 }
 
@@ -169,7 +169,7 @@
   cache.PostNotification(this, ax::mojom::Event::kHide);
   if (descendant) {
     cache.PostNotification(this, ax::mojom::Event::kChildrenChanged);
-    cache.MarkAXObjectDirtyWithCleanLayout(descendant, false);
+    cache.MarkAXObjectDirtyWithCleanLayout(descendant);
   }
 }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 72e7bfcb..69c4d328 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -4440,7 +4440,7 @@
 
     // Mark this node dirty. AXEventGenerator will automatically infer
     // that the active descendant changed.
-    AXObjectCache().MarkAXObjectDirtyWithCleanLayout(this, false);
+    AXObjectCache().MarkAXObjectDirtyWithCleanLayout(this);
   }
 }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 1295b25..2e37347f 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -1728,7 +1728,7 @@
   if (parent && ui::IsContainerWithSelectableChildren(parent->RoleValue()))
     return;
 
-  MarkAXObjectDirtyWithCleanLayout(obj, false);
+  MarkAXObjectDirtyWithCleanLayout(obj);
 }
 
 void AXObjectCacheImpl::TextChanged(Node* node) {
@@ -1793,7 +1793,7 @@
       }
     }
 
-    MarkAXObjectDirtyWithCleanLayout(obj, /*subtree=*/false);
+    MarkAXObjectDirtyWithCleanLayout(obj);
   }
 
   if (optional_node_for_relation_update)
@@ -1827,7 +1827,7 @@
   }
 
   // Refresh the focusable state and State::kIgnored on the exposed object.
-  MarkAXObjectDirtyWithCleanLayout(obj, false);
+  MarkAXObjectDirtyWithCleanLayout(obj);
 }
 
 void AXObjectCacheImpl::DocumentTitleChanged() {
@@ -2156,7 +2156,7 @@
   if (!element)
     return;
 
-  MarkElementDirty(element, false);
+  MarkElementDirty(element);
 }
 
 bool AXObjectCacheImpl::IsPopup(Document& document) const {
@@ -2298,7 +2298,7 @@
     }
 
     AXObject* new_object = refresh(object);
-    MarkAXObjectDirtyWithCleanLayout(new_object, false);
+    MarkAXObjectDirtyWithCleanLayout(new_object);
 
 #if defined(AX_FAIL_FAST_BUILD)
     SANITIZER_CHECK(!new_object ||
@@ -2588,7 +2588,7 @@
 
 void AXObjectCacheImpl::ImageLoaded(const LayoutObject* layout_object) {
   AXObject* obj = Get(layout_object);
-  MarkAXObjectDirty(obj, false);
+  MarkAXObjectDirty(obj);
 }
 
 void AXObjectCacheImpl::HandleClicked(Node* node) {
@@ -2631,7 +2631,7 @@
   AXObject* listbox = obj->ParentObjectUnignored();
   if (listbox && listbox->RoleValue() == ax::mojom::Role::kListBox) {
     // Ensure listbox options are in sync as selection status may have changed
-    MarkAXObjectDirty(listbox, true);
+    MarkAXSubtreeDirty(listbox);
     PostNotification(listbox, ax::mojom::Event::kSelectedChildrenChanged);
   }
 }
@@ -2824,7 +2824,7 @@
 
   // Invalidate the subtree because aria-hidden affects the
   // accessibility ignored state for the entire subtree.
-  MarkAXObjectDirtyWithCleanLayout(obj, /*subtree=*/true);
+  MarkAXSubtreeDirtyWithCleanLayout(obj);
   ChildrenChangedWithCleanLayout(obj->CachedParentObject());
 }
 
@@ -2865,14 +2865,14 @@
     FocusableChangedWithCleanLayout(element);
   } else if (attr_name == html_names::kDisabledAttr ||
              attr_name == html_names::kReadonlyAttr) {
-    MarkElementDirtyWithCleanLayout(element, false);
+    MarkElementDirtyWithCleanLayout(element);
   } else if (attr_name == html_names::kValueAttr) {
     HandleValueChanged(element);
   } else if (attr_name == html_names::kMinAttr ||
              attr_name == html_names::kMaxAttr) {
-    MarkElementDirtyWithCleanLayout(element, false);
+    MarkElementDirtyWithCleanLayout(element);
   } else if (attr_name == html_names::kStepAttr) {
-    MarkElementDirtyWithCleanLayout(element, false);
+    MarkElementDirtyWithCleanLayout(element);
   } else if (attr_name == html_names::kUsemapAttr) {
     HandleUseMapAttributeChangedWithCleanLayout(element);
   } else if (attr_name == html_names::kNameAttr) {
@@ -2908,9 +2908,9 @@
   } else if (attr_name == html_names::kAriaHiddenAttr) {
     HandleAriaHiddenChangedWithCleanLayout(element);
   } else if (attr_name == html_names::kAriaInvalidAttr) {
-    MarkElementDirtyWithCleanLayout(element, false);
+    MarkElementDirtyWithCleanLayout(element);
   } else if (attr_name == html_names::kAriaErrormessageAttr) {
-    MarkElementDirtyWithCleanLayout(element, false);
+    MarkElementDirtyWithCleanLayout(element);
   } else if (attr_name == html_names::kAriaOwnsAttr) {
     if (AXObject* obj = GetOrCreate(element))
       relation_cache_->UpdateAriaOwnsWithCleanLayout(obj);
@@ -3059,14 +3059,12 @@
 
   AXObject* message_ax_object = ValidationMessageObjectIfInvalid(
       /* Fire children changed on root if it gains message child */ true);
-  if (message_ax_object) {
-    MarkAXObjectDirtyWithCleanLayout(message_ax_object,
-                                     false);  // May be invisible now.
-  }
+  if (message_ax_object)  // May be invisible now.
+    MarkAXObjectDirtyWithCleanLayout(message_ax_object);
 
   // If the form control is invalid, it will now have an error message relation
   // to the message container.
-  MarkElementDirtyWithCleanLayout(form_control, false);
+  MarkElementDirtyWithCleanLayout(form_control);
 }
 
 void AXObjectCacheImpl::HandleEventListenerAdded(
@@ -3103,7 +3101,7 @@
   // If the |event_type| may affect the ignored state of |node|, which means
   // that the parent's children may have changed.
   modification_count_++;
-  MarkElementDirty(&node, /*subtree=*/false);
+  MarkElementDirty(&node);
 }
 
 void AXObjectCacheImpl::LabelChangedWithCleanLayout(Element* element) {
@@ -3209,10 +3207,8 @@
   }
 }
 
-void AXObjectCacheImpl::MarkAXObjectDirtyHelper(
-    AXObject* obj,
-    bool subtree,
-    ax::mojom::blink::Action event_from_action) {
+void AXObjectCacheImpl::MarkAXObjectDirtyWithCleanLayoutHelper(AXObject* obj,
+                                                               bool subtree) {
   if (!obj || obj->IsDetached() || !obj->GetDocument() ||
       !obj->GetDocument()->View() ||
       !obj->GetDocument()->View()->GetFrame().GetPage()) {
@@ -3221,53 +3217,45 @@
 
   WebLocalFrameImpl* webframe = WebLocalFrameImpl::FromFrame(
       obj->GetDocument()->AXObjectCacheOwner().GetFrame());
-  if (webframe && webframe->Client()) {
-    webframe->Client()->MarkWebAXObjectDirty(WebAXObject(obj), subtree,
-                                             event_from_action);
-  }
+  if (webframe && webframe->Client())
+    webframe->Client()->MarkWebAXObjectDirty(WebAXObject(obj), subtree);
+  obj->UpdateCachedAttributeValuesIfNeeded(true);
 }
 
-void AXObjectCacheImpl::MarkAXObjectDirtyWithCleanLayout(
-    AXObject* obj,
-    bool subtree,
-    ax::mojom::blink::Action event_from_action) {
+void AXObjectCacheImpl::MarkAXObjectDirtyWithCleanLayout(AXObject* obj) {
+  MarkAXObjectDirtyWithCleanLayoutHelper(obj, false);
+}
+
+void AXObjectCacheImpl::MarkAXSubtreeDirtyWithCleanLayout(AXObject* obj) {
+  MarkAXObjectDirtyWithCleanLayoutHelper(obj, true);
+}
+
+void AXObjectCacheImpl::MarkAXObjectDirty(AXObject* obj) {
   if (!obj)
     return;
-  MarkAXObjectDirtyHelper(obj, subtree, event_from_action);
-  UpdateCachedAttributeValuesWithCleanLayout(obj->GetNode(), obj);
+  base::OnceClosure callback =
+      WTF::Bind(&AXObjectCacheImpl::MarkAXObjectDirtyWithCleanLayout,
+                WrapWeakPersistent(this), WrapWeakPersistent(obj));
+  DeferTreeUpdateInternal(std::move(callback), obj);
 }
 
-void AXObjectCacheImpl::UpdateCachedAttributeValuesWithCleanLayout(
-    Node* node,
-    AXObject* obj) {
-  if (obj)
-    obj->UpdateCachedAttributeValuesIfNeeded(true);
-}
-
-void AXObjectCacheImpl::MarkAXObjectDirty(
-    AXObject* obj,
-    bool subtree,
-    ax::mojom::blink::Action event_from_action) {
+void AXObjectCacheImpl::MarkAXSubtreeDirty(AXObject* obj) {
   if (!obj)
     return;
-  MarkAXObjectDirtyHelper(obj, subtree, event_from_action);
-  if (obj->GetNode()) {
-    DeferTreeUpdate(
-        &AXObjectCacheImpl::UpdateCachedAttributeValuesWithCleanLayout, obj);
-  }
+  base::OnceClosure callback =
+      WTF::Bind(&AXObjectCacheImpl::MarkAXSubtreeDirtyWithCleanLayout,
+                WrapWeakPersistent(this), WrapWeakPersistent(obj));
+  DeferTreeUpdateInternal(std::move(callback), obj);
 }
 
-void AXObjectCacheImpl::MarkElementDirty(const Node* element, bool subtree) {
-  // Warning, if no AXObject exists for element, nothing is marked dirty,
-  // including descendant objects when subtree == true.
-  MarkAXObjectDirty(Get(element), subtree);
+void AXObjectCacheImpl::MarkElementDirty(const Node* element) {
+  // Warning, if no AXObject exists for element, nothing is marked dirty.
+  MarkAXObjectDirty(Get(element));
 }
 
-void AXObjectCacheImpl::MarkElementDirtyWithCleanLayout(const Node* element,
-                                                        bool subtree) {
-  // Warning, if no AXObject exists for element, nothing is marked dirty,
-  // including descendant objects when subtree == true.
-  MarkAXObjectDirtyWithCleanLayout(Get(element), subtree);
+void AXObjectCacheImpl::MarkElementDirtyWithCleanLayout(const Node* element) {
+  // Warning, if no AXObject exists for element, nothing is marked dirty.
+  MarkAXObjectDirtyWithCleanLayout(Get(element));
 }
 
 void AXObjectCacheImpl::HandleFocusedUIElementChanged(
@@ -3327,7 +3315,7 @@
 
   active_aria_modal_dialog_ = new_active_aria_modal;
   modification_count_++;
-  MarkAXObjectDirty(Root(), true);
+  MarkAXSubtreeDirty(Root());
 }
 
 AXObject* AXObjectCacheImpl::AncestorAriaModalDialog(Node* node) {
@@ -3437,7 +3425,7 @@
 
 void AXObjectCacheImpl::HandleUpdateActiveMenuOption(Node* menu_list) {
   if (!use_ax_menu_list_) {
-    MarkElementDirty(menu_list, false);
+    MarkElementDirty(menu_list);
     return;
   }
 
@@ -3462,7 +3450,7 @@
 
 void AXObjectCacheImpl::DidShowMenuListPopupWithCleanLayout(Node* menu_list) {
   if (!use_ax_menu_list_) {
-    MarkAXObjectDirtyWithCleanLayout(Get(menu_list), false);
+    MarkAXObjectDirtyWithCleanLayout(Get(menu_list));
     return;
   }
 
@@ -3481,7 +3469,7 @@
 
 void AXObjectCacheImpl::DidHideMenuListPopupWithCleanLayout(Node* menu_list) {
   if (!use_ax_menu_list_) {
-    MarkAXObjectDirtyWithCleanLayout(Get(menu_list), false);
+    MarkAXObjectDirtyWithCleanLayout(Get(menu_list));
     return;
   }
 
@@ -3539,7 +3527,7 @@
 }
 
 void AXObjectCacheImpl::HandleFrameRectsChanged(Document& document) {
-  MarkAXObjectDirty(Get(&document), false);
+  MarkElementDirty(&document);
 }
 
 void AXObjectCacheImpl::InvalidateBoundingBox(
@@ -3553,7 +3541,7 @@
   SCOPED_DISALLOW_LIFECYCLE_TRANSITION(*frame_view->GetFrame().GetDocument());
 
   InvalidateBoundingBoxForFixedOrStickyPosition();
-  MarkElementDirty(document_, false);
+  MarkElementDirty(document_);
   DeferTreeUpdate(&AXObjectCacheImpl::EnsurePostNotification, document_,
                   ax::mojom::blink::Event::kLayoutComplete);
 }
@@ -3564,7 +3552,7 @@
   InvalidateBoundingBoxForFixedOrStickyPosition();
   Node* node = GetClosestNodeForLayoutObject(layout_object);
   if (node) {
-    MarkElementDirty(node, false);
+    MarkElementDirty(node);
     DeferTreeUpdate(&AXObjectCacheImpl::EnsurePostNotification, node,
                     ax::mojom::blink::Event::kLayoutComplete);
   }
@@ -3721,7 +3709,7 @@
   WebAXAutofillState previous_state = GetAutofillState(id);
   if (state != previous_state) {
     autofill_state_map_.Set(id, state);
-    MarkAXObjectDirty(ObjectFromAXID(id), false);
+    MarkAXObjectDirty(ObjectFromAXID(id));
   }
 }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
index c001ba2..dbf5fc8 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
@@ -230,6 +230,9 @@
   void ChildrenChangedWithCleanLayout(Node* optional_node_for_relation_update,
                                       AXObject*);
 
+  void MarkAXObjectDirtyWithCleanLayout(AXObject*);
+  void MarkAXSubtreeDirtyWithCleanLayout(AXObject*);
+
   // When an object is created or its id changes, this must be called so that
   // the relation cache is updated.
   void MaybeNewRelationTarget(Node& node, AXObject* obj);
@@ -270,11 +273,6 @@
   // TODO(accessibility) Find out if we can merge with EnsurePostNotification().
   void PostNotification(Node*, ax::mojom::blink::Event);
   void PostNotification(AXObject*, ax::mojom::blink::Event);
-  void MarkAXObjectDirtyWithCleanLayout(
-      AXObject*,
-      bool subtree,
-      ax::mojom::blink::Action event_from_action =
-          ax::mojom::blink::Action::kNone);
 
   //
   // Aria-owns support.
@@ -435,17 +433,11 @@
 
   ax::mojom::blink::EventFrom ComputeEventFrom();
 
-  void UpdateCachedAttributeValuesWithCleanLayout(Node* node, AXObject* obj);
-  void MarkAXObjectDirtyHelper(AXObject* obj,
-                               bool subtree,
-                               ax::mojom::blink::Action event_from_action);
-  void MarkAXObjectDirty(AXObject*,
-                         bool subtree,
-                         ax::mojom::blink::Action event_from_action =
-                             ax::mojom::blink::Action::kNone);
-  void MarkElementDirty(const Node*, bool subtree);
-  void MarkAXSubtreeDirtyWithCleanLayout(AXObject*);
-  void MarkElementDirtyWithCleanLayout(const Node*, bool subtree);
+  void MarkAXObjectDirtyWithCleanLayoutHelper(AXObject* obj, bool subtree);
+  void MarkAXObjectDirty(AXObject*);
+  void MarkAXSubtreeDirty(AXObject*);
+  void MarkElementDirty(const Node*);
+  void MarkElementDirtyWithCleanLayout(const Node*);
 
   // Helper that clears children up to the first included ancestor and returns
   // the ancestor if a children changed notification should be fired on it.
diff --git a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
index bd8e155..201b178 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
@@ -488,18 +488,18 @@
     HeapVector<Member<AXObject>> related_sources;
     GetReverseRelated(current_node, related_sources);
     for (AXObject* related : related_sources) {
-      if (related) {
-        object_cache_->MarkAXObjectDirtyWithCleanLayout(related,
-                                                        /*subtree=*/false);
-      }
+      if (related && related->AccessibilityIsIncludedInTree())
+        object_cache_->MarkAXObjectDirtyWithCleanLayout(related);
     }
 
     // Ancestors that may derive their accessible name from descendant content
     // should also handle text changed events when descendant content changes.
     if (current_node != node) {
       AXObject* obj = Get(current_node);
-      if (obj && obj->SupportsNameFromContents(/*recursive=*/false))
-        object_cache_->MarkAXObjectDirtyWithCleanLayout(obj, /*subtree=*/false);
+      if (obj && obj->AccessibilityIsIncludedInTree() &&
+          obj->SupportsNameFromContents(/*recursive=*/false)) {
+        object_cache_->MarkAXObjectDirtyWithCleanLayout(obj);
+      }
     }
 
     // Forward relation via <label for="[id]">.
@@ -559,9 +559,9 @@
       To<HTMLElement>(node)->FastGetAttribute(html_names::kForAttr);
   if (!id.IsEmpty()) {
     all_previously_seen_label_target_ids_.insert(id);
-    if (auto* control = To<HTMLLabelElement>(node)->control()) {
-      if (AXObject* obj = Get(control))
-        object_cache_->MarkAXObjectDirtyWithCleanLayout(obj, /*subtree=*/false);
+    if (AXObject* obj = Get(To<HTMLLabelElement>(node)->control())) {
+      if (obj->AccessibilityIsIncludedInTree())
+        object_cache_->MarkAXObjectDirtyWithCleanLayout(obj);
     }
   }
 }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_slider.cc b/third_party/blink/renderer/modules/accessibility/ax_slider.cc
index bd83a72..b8aed3a 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_slider.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_slider.cc
@@ -87,7 +87,7 @@
     return false;
 
   // Ensure the AX node is updated.
-  AXObjectCache().MarkAXObjectDirtyWithCleanLayout(this, false);
+  AXObjectCache().MarkAXObjectDirtyWithCleanLayout(this);
 
   return true;
 }
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
index a4ea7de..540d09188 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -119,13 +119,58 @@
 
   ValidateStateStack();
 
-  DCHECK_GE(state_stack_.size(), 1u);
+  DCHECK_GT(state_stack_.size(), static_cast<WTF::wtf_size_t>(layer_count_));
   if (state_stack_.size() <= 1)
     return;
+
+  // Verify that the top of the stack was pushed with Save.
+  if (RuntimeEnabledFeatures::Canvas2dLayersEnabled() &&
+      state_stack_.back()->GetSaveType() !=
+          CanvasRenderingContext2DState::SaveType::kSaveRestore) {
+    return;
+  }
+
   // Verify that the current state's transform is invertible.
   if (GetState().IsTransformInvertible())
     path_.Transform(GetState().GetTransform());
 
+  PopAndRestore();
+}
+
+void BaseRenderingContext2D::beginLayer() {
+  // TODO(crbug.com/1220266): Implementation coming later.
+  return;
+}
+
+void BaseRenderingContext2D::endLayer() {
+  if (isContextLost())
+    return;
+
+  ValidateStateStack();
+
+  DCHECK_GT(state_stack_.size(), static_cast<WTF::wtf_size_t>(layer_count_));
+  if (state_stack_.size() <= 1 || layer_count_ <= 0)
+    return;
+
+  // Verify that the current state's transform is invertible.
+  if (GetState().IsTransformInvertible())
+    path_.Transform(GetState().GetTransform());
+
+  // All saves performed since the last beginLayer are no-ops.
+  while (state_stack_.back()->GetSaveType() ==
+         CanvasRenderingContext2DState::SaveType::kSaveRestore) {
+    PopAndRestore();
+  }
+
+  DCHECK_GE(state_stack_.back()->GetSaveType(),
+            CanvasRenderingContext2DState::SaveType::kBeginEndLayer);
+  PopAndRestore();
+  layer_count_--;
+}
+
+void BaseRenderingContext2D::PopAndRestore() {
+  DCHECK_GE(state_stack_.size(), 1u);
+
   state_stack_.pop_back();
   state_stack_.back()->ClearResolvedFilter();
 
@@ -140,14 +185,6 @@
   ValidateStateStack();
 }
 
-void BaseRenderingContext2D::beginLayer() {
-  // TODO(crbug.com/1220266): Implementation coming later.
-}
-
-void BaseRenderingContext2D::endLayer() {
-  // TODO(crbug.com/1220266): Implementation coming later.
-}
-
 void BaseRenderingContext2D::RestoreMatrixClipStack(cc::PaintCanvas* c) const {
   if (!c)
     return;
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
index 94edc0a..3cff8a8 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
@@ -449,6 +449,8 @@
                      DrawType);
 
   HeapVector<Member<CanvasRenderingContext2DState>> state_stack_;
+  // Counts how many states have been pushed with BeginLayer.
+  int layer_count_ = 0;
   AntiAliasingMode clip_antialiasing_;
 
   virtual void FinalizeFrame() {}
@@ -498,6 +500,11 @@
   IdentifiabilityStudyHelper identifiability_study_helper_;
 
  private:
+  // Pops from the top of the state stack, inverts transform, restores the
+  // PaintCanvas, and validates the state stack. Helper for Restore and
+  // EndLayer.
+  void PopAndRestore();
+
   bool ShouldDrawImageAntialiased(const FloatRect& dest_rect) const;
 
   // When the canvas is stroked or filled with a pattern, which is assumed to
diff --git a/third_party/blink/renderer/modules/csspaint/BUILD.gn b/third_party/blink/renderer/modules/csspaint/BUILD.gn
index 54aeb7e6..d0e4f4f 100644
--- a/third_party/blink/renderer/modules/csspaint/BUILD.gn
+++ b/third_party/blink/renderer/modules/csspaint/BUILD.gn
@@ -25,6 +25,7 @@
     "native_paint_worklet.cc",
     "native_paint_worklet.h",
     "native_paint_worklet_proxy_client.h",
+    "paint_definition.h",
     "paint_rendering_context_2d.cc",
     "paint_rendering_context_2d.h",
     "paint_size.h",
diff --git a/third_party/blink/renderer/modules/csspaint/css_paint_definition.cc b/third_party/blink/renderer/modules/csspaint/css_paint_definition.cc
index 2b80185..5be3c03 100644
--- a/third_party/blink/renderer/modules/csspaint/css_paint_definition.cc
+++ b/third_party/blink/renderer/modules/csspaint/css_paint_definition.cc
@@ -10,6 +10,9 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_no_argument_constructor.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_paint_callback.h"
 #include "third_party/blink/renderer/core/css/css_computed_style_declaration.h"
+#include "third_party/blink/renderer/core/css/cssom/cross_thread_color_value.h"
+#include "third_party/blink/renderer/core/css/cssom/cross_thread_unit_value.h"
+#include "third_party/blink/renderer/core/css/cssom/css_paint_worklet_input.h"
 #include "third_party/blink/renderer/core/css/cssom/prepopulated_computed_style_property_map.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
@@ -53,6 +56,33 @@
 
 CSSPaintDefinition::~CSSPaintDefinition() = default;
 
+// PaintDefinition override
+sk_sp<PaintRecord> CSSPaintDefinition::Paint(
+    const CompositorPaintWorkletInput* compositor_input,
+    const CompositorPaintWorkletJob::AnimatedPropertyValues&
+        animated_property_values) {
+  // TODO(crbug.com/1227698): Use To<> with checks instead of static_cast.
+  const CSSPaintWorkletInput* input =
+      static_cast<const CSSPaintWorkletInput*>(compositor_input);
+  PaintWorkletStylePropertyMap* style_map =
+      MakeGarbageCollected<PaintWorkletStylePropertyMap>(input->StyleMapData());
+  CSSStyleValueVector paint_arguments;
+  for (const auto& style_value : input->ParsedInputArguments()) {
+    paint_arguments.push_back(style_value->ToCSSStyleValue());
+  }
+
+  ApplyAnimatedPropertyOverrides(style_map, animated_property_values);
+
+  sk_sp<PaintRecord> result =
+      Paint(FloatSize(input->GetSize()), input->EffectiveZoom(), style_map,
+            &paint_arguments, input->DeviceScaleFactor());
+
+  // Return empty record if paint fails.
+  if (!result)
+    result = sk_make_sp<PaintRecord>();
+  return result;
+}
+
 sk_sp<PaintRecord> CSSPaintDefinition::Paint(
     const FloatSize& container_size,
     float zoom,
@@ -95,6 +125,44 @@
   return rendering_context->GetRecord();
 }
 
+void CSSPaintDefinition::ApplyAnimatedPropertyOverrides(
+    PaintWorkletStylePropertyMap* style_map,
+    const CompositorPaintWorkletJob::AnimatedPropertyValues&
+        animated_property_values) {
+  for (const auto& property_value : animated_property_values) {
+    DCHECK(property_value.second.has_value());
+    String property_name(
+        property_value.first.custom_property_name.value().c_str());
+    DCHECK(style_map->StyleMapData().Contains(property_name));
+    CrossThreadStyleValue* old_value =
+        style_map->StyleMapData().at(property_name);
+    switch (old_value->GetType()) {
+      case CrossThreadStyleValue::StyleValueType::kUnitType: {
+        DCHECK(property_value.second.float_value);
+        std::unique_ptr<CrossThreadUnitValue> new_value =
+            std::make_unique<CrossThreadUnitValue>(
+                property_value.second.float_value.value(),
+                DynamicTo<CrossThreadUnitValue>(old_value)->GetUnitType());
+        style_map->StyleMapData().Set(property_name, std::move(new_value));
+        break;
+      }
+      case CrossThreadStyleValue::StyleValueType::kColorType: {
+        DCHECK(property_value.second.color_value);
+        SkColor sk_color = property_value.second.color_value.value();
+        Color color(MakeRGBA(SkColorGetR(sk_color), SkColorGetG(sk_color),
+                             SkColorGetB(sk_color), SkColorGetA(sk_color)));
+        std::unique_ptr<CrossThreadColorValue> new_value =
+            std::make_unique<CrossThreadColorValue>(color);
+        style_map->StyleMapData().Set(property_name, std::move(new_value));
+        break;
+      }
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+}
+
 void CSSPaintDefinition::MaybeCreatePaintInstance() {
   if (did_call_constructor_)
     return;
@@ -115,6 +183,7 @@
   visitor->Trace(instance_);
   visitor->Trace(context_settings_);
   visitor->Trace(script_state_);
+  PaintDefinition::Trace(visitor);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/csspaint/css_paint_definition.h b/third_party/blink/renderer/modules/csspaint/css_paint_definition.h
index 9918d32..ccc5ba1 100644
--- a/third_party/blink/renderer/modules/csspaint/css_paint_definition.h
+++ b/third_party/blink/renderer/modules/csspaint/css_paint_definition.h
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/css/css_syntax_definition.h"
 #include "third_party/blink/renderer/core/css/cssom/css_style_value.h"
+#include "third_party/blink/renderer/modules/csspaint/paint_definition.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/name_client.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@@ -21,6 +22,7 @@
 
 namespace blink {
 
+class PaintWorkletStylePropertyMap;
 class ScriptState;
 class StylePropertyMapReadOnly;
 class V8NoArgumentConstructor;
@@ -31,7 +33,8 @@
 // types as well.
 class MODULES_EXPORT CSSPaintDefinition final
     : public GarbageCollected<CSSPaintDefinition>,
-      public NameClient {
+      public NameClient,
+      public PaintDefinition {
  public:
   CSSPaintDefinition(
       ScriptState*,
@@ -41,7 +44,12 @@
       const Vector<AtomicString>& custom_invalidation_properties,
       const Vector<CSSSyntaxDefinition>& input_argument_types,
       const PaintRenderingContext2DSettings*);
-  ~CSSPaintDefinition() final;
+  ~CSSPaintDefinition() override;
+
+  // PaintDefinition override
+  sk_sp<PaintRecord> Paint(
+      const CompositorPaintWorkletInput*,
+      const CompositorPaintWorkletJob::AnimatedPropertyValues&) override;
 
   // Invokes the javascript 'paint' callback on an instance of the javascript
   // class. The size given will be the size of the PaintRenderingContext2D
@@ -72,13 +80,17 @@
 
   ScriptState* GetScriptState() const { return script_state_; }
 
-  virtual void Trace(Visitor* visitor) const;
+  void Trace(Visitor* visitor) const override;
   const char* NameInHeapSnapshot() const override {
     return "CSSPaintDefinition";
   }
 
  private:
   void MaybeCreatePaintInstance();
+  void ApplyAnimatedPropertyOverrides(
+      PaintWorkletStylePropertyMap* style_map,
+      const CompositorPaintWorkletJob::AnimatedPropertyValues&
+          animated_property_values);
 
   Member<ScriptState> script_state_;
 
diff --git a/third_party/blink/renderer/modules/csspaint/paint_definition.h b/third_party/blink/renderer/modules/csspaint/paint_definition.h
new file mode 100644
index 0000000..5c434e2
--- /dev/null
+++ b/third_party/blink/renderer/modules/csspaint/paint_definition.h
@@ -0,0 +1,28 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_CSSPAINT_PAINT_DEFINITION_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_CSSPAINT_PAINT_DEFINITION_H_
+
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/graphics/platform_paint_worklet_layer_painter.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+
+namespace blink {
+
+class MODULES_EXPORT PaintDefinition : public GarbageCollectedMixin {
+ public:
+  virtual ~PaintDefinition() = default;
+
+  virtual sk_sp<PaintRecord> Paint(
+      const CompositorPaintWorkletInput*,
+      const CompositorPaintWorkletJob::AnimatedPropertyValues&) = 0;
+
+  void Trace(Visitor* visitor) const override {}
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_CSSPAINT_PAINT_DEFINITION_H_
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc
index b742b961..c08204c 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc
@@ -186,72 +186,13 @@
   PaintWorkletGlobalScope* global_scope = global_scopes_[base::RandInt(
       0, (PaintWorklet::kNumGlobalScopesPerThread)-1)];
 
+  // TODO(crbug.com/1227698): Use To<> with checks instead of static_cast.
   const CSSPaintWorkletInput* input =
       static_cast<const CSSPaintWorkletInput*>(compositor_input);
   CSSPaintDefinition* definition =
       global_scope->FindDefinition(input->NameCopy());
-  PaintWorkletStylePropertyMap* style_map =
-      MakeGarbageCollected<PaintWorkletStylePropertyMap>(input->StyleMapData());
-
-  CSSStyleValueVector paint_arguments;
-  for (const auto& style_value : input->ParsedInputArguments()) {
-    paint_arguments.push_back(style_value->ToCSSStyleValue());
-  }
-
-  ApplyAnimatedPropertyOverrides(style_map, animated_property_values);
-
   device_pixel_ratio_ = input->DeviceScaleFactor() * input->EffectiveZoom();
-
-  sk_sp<PaintRecord> result = definition->Paint(
-      FloatSize(input->GetSize()), input->EffectiveZoom(), style_map,
-      &paint_arguments, input->DeviceScaleFactor());
-
-  // CSSPaintDefinition::Paint returns nullptr if it fails, but for
-  // OffThread-PaintWorklet we prefer to insert empty PaintRecords into the
-  // cache. Do the conversion here.
-  // TODO(smcgruer): Once OffThread-PaintWorklet launches, we can make
-  // CSSPaintDefinition::Paint return empty PaintRecords.
-  if (!result)
-    result = sk_make_sp<PaintRecord>();
-  return result;
-}
-
-void PaintWorkletProxyClient::ApplyAnimatedPropertyOverrides(
-    PaintWorkletStylePropertyMap* style_map,
-    const CompositorPaintWorkletJob::AnimatedPropertyValues&
-        animated_property_values) {
-  for (const auto& property_value : animated_property_values) {
-    DCHECK(property_value.second.has_value());
-    String property_name(
-        property_value.first.custom_property_name.value().c_str());
-    DCHECK(style_map->StyleMapData().Contains(property_name));
-    CrossThreadStyleValue* old_value =
-        style_map->StyleMapData().at(property_name);
-    switch (old_value->GetType()) {
-      case CrossThreadStyleValue::StyleValueType::kUnitType: {
-        DCHECK(property_value.second.float_value);
-        std::unique_ptr<CrossThreadUnitValue> new_value =
-            std::make_unique<CrossThreadUnitValue>(
-                property_value.second.float_value.value(),
-                DynamicTo<CrossThreadUnitValue>(old_value)->GetUnitType());
-        style_map->StyleMapData().Set(property_name, std::move(new_value));
-        break;
-      }
-      case CrossThreadStyleValue::StyleValueType::kColorType: {
-        DCHECK(property_value.second.color_value);
-        SkColor sk_color = property_value.second.color_value.value();
-        Color color(MakeRGBA(SkColorGetR(sk_color), SkColorGetG(sk_color),
-                             SkColorGetB(sk_color), SkColorGetA(sk_color)));
-        std::unique_ptr<CrossThreadColorValue> new_value =
-            std::make_unique<CrossThreadColorValue>(color);
-        style_map->StyleMapData().Set(property_name, std::move(new_value));
-        break;
-      }
-      default:
-        NOTREACHED();
-        break;
-    }
-  }
+  return definition->Paint(compositor_input, animated_property_values);
 }
 
 void ProvidePaintWorkletProxyClientTo(WorkerClients* clients,
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h
index d0a106f..a7d714d 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h
@@ -101,11 +101,6 @@
   FRIEND_TEST_ALL_PREFIXES(PaintWorkletProxyClientTest,
                            PaintWorkletProxyClientConstruction);
 
-  void ApplyAnimatedPropertyOverrides(
-      PaintWorkletStylePropertyMap* style_map,
-      const CompositorPaintWorkletJob::AnimatedPropertyValues&
-          animated_property_values);
-
   // Store the device pixel ratio here so it can be used off main thread
   double device_pixel_ratio_;
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_data_channel.cc b/third_party/blink/renderer/modules/peerconnection/rtc_data_channel.cc
index 9c531e9..3405045 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_data_channel.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_data_channel.cc
@@ -29,6 +29,7 @@
 #include <string>
 #include <utility>
 
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/core/events/message_event.h"
@@ -64,39 +65,60 @@
 
 namespace {
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
 enum class DataChannelCounters {
-  kCreated,
-  kOpened,
-  kReliable,
-  kOrdered,
-  kNegotiated,
-  kBoundary
+  kCreated = 0,
+  kOpened = 1,
+  kReliable = 2,
+  kOrdered = 3,
+  kNegotiated = 4,
+  kMaxValue = kNegotiated,
+};
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class DataChannelAggregateType {
+  kUnReliableUnordered = 0,
+  kUnReliableOrdered = 1,
+  kReliableUnordered = 2,
+  kReliableOrdered = 3,
+  kMaxValue = kReliableOrdered,
 };
 
 void IncrementCounter(DataChannelCounters counter) {
-  UMA_HISTOGRAM_ENUMERATION("WebRTC.DataChannelCounters", counter,
-                            DataChannelCounters::kBoundary);
+  base::UmaHistogramEnumeration("WebRTC.DataChannelCounters", counter);
 }
 
 void IncrementCounters(const webrtc::DataChannelInterface& channel) {
+  int aggregate_type = 0;
+
   IncrementCounter(DataChannelCounters::kCreated);
-  if (channel.reliable())
+  if (channel.reliable()) {
     IncrementCounter(DataChannelCounters::kReliable);
-  if (channel.ordered())
+    aggregate_type += 2;
+  }
+  if (channel.ordered()) {
     IncrementCounter(DataChannelCounters::kOrdered);
+    aggregate_type += 1;
+  }
   if (channel.negotiated())
     IncrementCounter(DataChannelCounters::kNegotiated);
 
+  base::UmaHistogramEnumeration(
+      "WebRTC.DataChannelAggregateType",
+      static_cast<DataChannelAggregateType>(aggregate_type));
+
   // Only record max retransmits and max packet life time if set.
   if (channel.maxRetransmitsOpt()) {
-    UMA_HISTOGRAM_CUSTOM_COUNTS("WebRTC.DataChannelMaxRetransmits",
-                                *(channel.maxRetransmitsOpt()), 1,
-                                std::numeric_limits<uint16_t>::max(), 50);
+    base::UmaHistogramCustomCounts("WebRTC.DataChannelMaxRetransmits",
+                                   *(channel.maxRetransmitsOpt()), 1,
+                                   std::numeric_limits<uint16_t>::max(), 50);
   }
   if (channel.maxPacketLifeTime()) {
-    UMA_HISTOGRAM_CUSTOM_COUNTS("WebRTC.DataChannelMaxPacketLifeTime",
-                                *channel.maxPacketLifeTime(), 1,
-                                std::numeric_limits<uint16_t>::max(), 50);
+    base::UmaHistogramCustomCounts("WebRTC.DataChannelMaxPacketLifeTime",
+                                   *channel.maxPacketLifeTime(), 1,
+                                   std::numeric_limits<uint16_t>::max(), 50);
   }
 }
 
@@ -125,6 +147,40 @@
   }
 }
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class DataChannelSctpErrorCode {
+  kUnspecified = 0,
+  kInvalidStreamIdentifier = 1,
+  kMissingMandatoryParameter = 2,
+  kStaleCookieError = 3,
+  kOutOfResource = 4,
+  kUnresolvableAddress = 5,
+  kUnrecognizedChunkType = 6,
+  kInvalidMandatoryParameter = 7,
+  kUnrecognizedParameters = 8,
+  kNoUserData = 9,
+  kCookieReceivedWhileShuttingDown = 10,
+  kRestartWithNewAddresses = 11,
+  kUserInitiatedAbort = 12,
+  kProtocolViolation = 13,
+  kOther = 14,
+  kMaxValue = kOther,
+};
+
+void IncrementErrorCounter(const webrtc::RTCError& error) {
+  DataChannelSctpErrorCode uma_code;
+  auto code = error.sctp_cause_code();
+  if (!code.has_value()) {
+    uma_code = DataChannelSctpErrorCode::kUnspecified;
+  } else if (*code >= static_cast<int>(DataChannelSctpErrorCode::kOther)) {
+    uma_code = DataChannelSctpErrorCode::kOther;
+  } else {
+    uma_code = static_cast<DataChannelSctpErrorCode>(*code);
+  }
+  base::UmaHistogramEnumeration("WebRTC.DataChannelSctpError", uma_code);
+}
+
 void SendOnSignalingThread(
     const scoped_refptr<webrtc::DataChannelInterface> channel,
     const webrtc::DataBuffer data_buffer) {
@@ -547,14 +603,19 @@
         DispatchEvent(*Event::Create(event_type_names::kClosing));
       }
       break;
-    case webrtc::DataChannelInterface::kClosed:
+    case webrtc::DataChannelInterface::kClosed: {
       feature_handle_for_scheduler_.reset();
-      if (!channel()->error().ok()) {
+      auto error = channel()->error();
+      if (!error.ok()) {
+        LOG(ERROR) << "DataChannel error: \"" << error.message() << "\""
+                   << ", code: " << error.sctp_cause_code().value_or(-1);
+        IncrementErrorCounter(error);
         DispatchEvent(*MakeGarbageCollected<RTCErrorEvent>(
-            event_type_names::kError, channel()->error()));
+            event_type_names::kError, error));
       }
       DispatchEvent(*Event::Create(event_type_names::kClose));
       break;
+    }
     default:
       break;
   }
diff --git a/third_party/blink/renderer/modules/webgl/webgl_webcodecs_video_frame.h b/third_party/blink/renderer/modules/webgl/webgl_webcodecs_video_frame.h
index 644d01c..cac7c86 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_webcodecs_video_frame.h
+++ b/third_party/blink/renderer/modules/webgl/webgl_webcodecs_video_frame.h
@@ -10,6 +10,10 @@
 #include "third_party/blink/renderer/modules/webcodecs/video_frame.h"
 #include "third_party/blink/renderer/modules/webgl/webgl_extension.h"
 
+namespace base {
+class WaitableEvent;
+}  // namespace base
+
 namespace media {
 class GpuMemoryBufferVideoFramePool;
 }  // namespace media
diff --git a/third_party/blink/renderer/platform/graphics/image.cc b/third_party/blink/renderer/platform/graphics/image.cc
index 16ee18e..5876fbb 100644
--- a/third_party/blink/renderer/platform/graphics/image.cc
+++ b/third_party/blink/renderer/platform/graphics/image.cc
@@ -33,6 +33,7 @@
 #include "base/numerics/checked_math.h"
 #include "build/build_config.h"
 #include "cc/tiles/software_image_decode_cache.h"
+#include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_data.h"
 #include "third_party/blink/renderer/platform/geometry/float_point.h"
@@ -300,6 +301,10 @@
   }
 }
 
+mojom::blink::ImageAnimationPolicy Image::AnimationPolicy() {
+  return mojom::blink::ImageAnimationPolicy::kImageAnimationPolicyAllowed;
+}
+
 scoped_refptr<Image> Image::ImageForDefaultFrame() {
   scoped_refptr<Image> image(this);
 
diff --git a/third_party/blink/renderer/platform/graphics/image.h b/third_party/blink/renderer/platform/graphics/image.h
index 4ef1b1a..04094f1 100644
--- a/third_party/blink/renderer/platform/graphics/image.h
+++ b/third_party/blink/renderer/platform/graphics/image.h
@@ -29,7 +29,7 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
-#include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom-blink.h"
+#include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom-blink-forward.h"
 #include "third_party/blink/renderer/platform/geometry/float_point.h"
 #include "third_party/blink/renderer/platform/geometry/float_size.h"
 #include "third_party/blink/renderer/platform/geometry/int_rect.h"
@@ -205,9 +205,7 @@
 
   // Set animationPolicy
   virtual void SetAnimationPolicy(mojom::blink::ImageAnimationPolicy) {}
-  virtual mojom::blink::ImageAnimationPolicy AnimationPolicy() {
-    return mojom::blink::ImageAnimationPolicy::kImageAnimationPolicyAllowed;
-  }
+  virtual mojom::blink::ImageAnimationPolicy AnimationPolicy();
 
   // Advances an animated image. For BitmapImage (e.g., animated gifs) this
   // will advance to the next frame. For SVGImage, this will trigger an
diff --git a/third_party/blink/renderer/platform/graphics/paint/display_item_list.h b/third_party/blink/renderer/platform/graphics/paint/display_item_list.h
index 687c374..766e629 100644
--- a/third_party/blink/renderer/platform/graphics/paint/display_item_list.h
+++ b/third_party/blink/renderer/platform/graphics/paint/display_item_list.h
@@ -5,6 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DISPLAY_ITEM_LIST_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DISPLAY_ITEM_LIST_H_
 
+#include <cstring>  // memcpy, memset
+#include <type_traits>
+
 #include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
 #include "third_party/blink/renderer/platform/graphics/paint/scrollbar_display_item.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
@@ -219,6 +222,10 @@
 #endif  // DCHECK_IS_ON()
 
  private:
+  static_assert(std::is_trivially_copyable<value_type>::value,
+                "DisplayItemList uses `memcpy` in several member functions; "
+                "the `value_type` used by it must be trivially copyable");
+
   ItemSlot* AllocateItemSlot() {
     if (items_.IsEmpty())
       items_.ReserveCapacity(initial_capacity_);
diff --git a/third_party/blink/renderer/platform/media/buffered_data_source_host_impl.cc b/third_party/blink/renderer/platform/media/buffered_data_source_host_impl.cc
index 82ef8c2c..c4ea2a8 100644
--- a/third_party/blink/renderer/platform/media/buffered_data_source_host_impl.cc
+++ b/third_party/blink/renderer/platform/media/buffered_data_source_host_impl.cc
@@ -6,7 +6,7 @@
 
 #include "media/base/timestamp_constants.h"
 
-namespace media {
+namespace blink {
 
 // We want a relatively small window for estimating bandwidth,
 // that way we don't need to worry too much about seeks and pause
@@ -179,4 +179,4 @@
   tick_clock_ = tick_clock;
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/buffered_data_source_host_impl_unittest.cc b/third_party/blink/renderer/platform/media/buffered_data_source_host_impl_unittest.cc
index d5aea3b8..0c49f72 100644
--- a/third_party/blink/renderer/platform/media/buffered_data_source_host_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/media/buffered_data_source_host_impl_unittest.cc
@@ -8,7 +8,7 @@
 #include "base/test/simple_test_tick_clock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace media {
+namespace blink {
 
 class BufferedDataSourceHostImplTest : public testing::Test {
  public:
@@ -153,4 +153,4 @@
                                    base::TimeDelta::FromSecondsD(1000.0), 1.0));
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/cache_util.cc b/third_party/blink/renderer/platform/media/cache_util.cc
index 497cc49..ec24b24 100644
--- a/third_party/blink/renderer/platform/media/cache_util.cc
+++ b/third_party/blink/renderer/platform/media/cache_util.cc
@@ -17,12 +17,11 @@
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_url_response.h"
 
-using base::Time;
-using base::TimeDelta;
-using net::HttpVersion;
-using blink::WebURLResponse;
+namespace blink {
 
-namespace media {
+using ::base::Time;
+using ::base::TimeDelta;
+using ::net::HttpVersion;
 
 enum { kHttpOK = 200, kHttpPartialContent = 206 };
 
@@ -127,4 +126,4 @@
   return ret;
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/cache_util.h b/third_party/blink/renderer/platform/media/cache_util.h
index 98f15c5..e7db731 100644
--- a/third_party/blink/renderer/platform/media/cache_util.h
+++ b/third_party/blink/renderer/platform/media/cache_util.h
@@ -12,9 +12,6 @@
 
 namespace blink {
 class WebURLResponse;
-}
-
-namespace media {
 
 // Reasons that a cached WebURLResponse will *not* prevent a future request to
 // the server.  Reported via UMA, so don't change/reuse previously-existing
@@ -43,6 +40,6 @@
 PLATFORM_EXPORT base::TimeDelta GetCacheValidUntil(
     const blink::WebURLResponse& response);
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_CACHE_UTIL_H_
diff --git a/third_party/blink/renderer/platform/media/cache_util_unittest.cc b/third_party/blink/renderer/platform/media/cache_util_unittest.cc
index 0bc6e47..754f261 100644
--- a/third_party/blink/renderer/platform/media/cache_util_unittest.cc
+++ b/third_party/blink/renderer/platform/media/cache_util_unittest.cc
@@ -19,10 +19,7 @@
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_url_response.h"
 
-using blink::WebString;
-using blink::WebURLResponse;
-
-namespace media {
+namespace blink {
 
 // Inputs & expected output for GetReasonsForUncacheability.
 struct GRFUTestCase {
@@ -83,4 +80,4 @@
   }
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/cdm_result_promise.h b/third_party/blink/renderer/platform/media/cdm_result_promise.h
index c4a6bda..6a5b7be 100644
--- a/third_party/blink/renderer/platform/media/cdm_result_promise.h
+++ b/third_party/blink/renderer/platform/media/cdm_result_promise.h
@@ -14,7 +14,7 @@
 #include "third_party/blink/renderer/platform/media/cdm_result_promise_helper.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 
-namespace media {
+namespace blink {
 
 const char kTimeUMAPrefix[] = "TimeTo.";
 
@@ -115,6 +115,6 @@
                                     blink::WebString::FromUTF8(error_message));
 }
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_CDM_RESULT_PROMISE_H_
diff --git a/third_party/blink/renderer/platform/media/cdm_result_promise_helper.cc b/third_party/blink/renderer/platform/media/cdm_result_promise_helper.cc
index accfbb8..a03c66e 100644
--- a/third_party/blink/renderer/platform/media/cdm_result_promise_helper.cc
+++ b/third_party/blink/renderer/platform/media/cdm_result_promise_helper.cc
@@ -7,18 +7,18 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/notreached.h"
 
-namespace media {
+namespace blink {
 
 CdmResultForUMA ConvertCdmExceptionToResultForUMA(
-    CdmPromise::Exception exception_code) {
+    media::CdmPromise::Exception exception_code) {
   switch (exception_code) {
-    case CdmPromise::Exception::NOT_SUPPORTED_ERROR:
+    case media::CdmPromise::Exception::NOT_SUPPORTED_ERROR:
       return NOT_SUPPORTED_ERROR;
-    case CdmPromise::Exception::INVALID_STATE_ERROR:
+    case media::CdmPromise::Exception::INVALID_STATE_ERROR:
       return INVALID_STATE_ERROR;
-    case CdmPromise::Exception::QUOTA_EXCEEDED_ERROR:
+    case media::CdmPromise::Exception::QUOTA_EXCEEDED_ERROR:
       return QUOTA_EXCEEDED_ERROR;
-    case CdmPromise::Exception::TYPE_ERROR:
+    case media::CdmPromise::Exception::TYPE_ERROR:
       return TYPE_ERROR;
   }
   NOTREACHED();
@@ -26,15 +26,15 @@
 }
 
 blink::WebContentDecryptionModuleException ConvertCdmException(
-    CdmPromise::Exception exception_code) {
+    media::CdmPromise::Exception exception_code) {
   switch (exception_code) {
-    case CdmPromise::Exception::NOT_SUPPORTED_ERROR:
+    case media::CdmPromise::Exception::NOT_SUPPORTED_ERROR:
       return blink::kWebContentDecryptionModuleExceptionNotSupportedError;
-    case CdmPromise::Exception::INVALID_STATE_ERROR:
+    case media::CdmPromise::Exception::INVALID_STATE_ERROR:
       return blink::kWebContentDecryptionModuleExceptionInvalidStateError;
-    case CdmPromise::Exception::QUOTA_EXCEEDED_ERROR:
+    case media::CdmPromise::Exception::QUOTA_EXCEEDED_ERROR:
       return blink::kWebContentDecryptionModuleExceptionQuotaExceededError;
-    case CdmPromise::Exception::TYPE_ERROR:
+    case media::CdmPromise::Exception::TYPE_ERROR:
       return blink::kWebContentDecryptionModuleExceptionTypeError;
   }
   NOTREACHED();
@@ -78,4 +78,4 @@
   base::UmaHistogramEnumeration(uma_name, result, NUM_RESULT_CODES);
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/cdm_result_promise_helper.h b/third_party/blink/renderer/platform/media/cdm_result_promise_helper.h
index 30f4893..80a0161c 100644
--- a/third_party/blink/renderer/platform/media/cdm_result_promise_helper.h
+++ b/third_party/blink/renderer/platform/media/cdm_result_promise_helper.h
@@ -12,7 +12,7 @@
 #include "third_party/blink/public/platform/web_content_decryption_module_result.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 
-namespace media {
+namespace blink {
 
 // A superset of media::ContentDecryptionModule::Exception for UMA reporting.
 // These values should never be changed as it will affect existing reporting,
@@ -45,6 +45,6 @@
                                         uint32_t system_code,
                                         CdmResultForUMA result);
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_CDM_RESULT_PROMISE_HELPER_H_
diff --git a/third_party/blink/renderer/platform/media/cdm_session_adapter.cc b/third_party/blink/renderer/platform/media/cdm_session_adapter.cc
index 14f2356..95b2c01 100644
--- a/third_party/blink/renderer/platform/media/cdm_session_adapter.cc
+++ b/third_party/blink/renderer/platform/media/cdm_session_adapter.cc
@@ -21,8 +21,7 @@
 #include "media/cdm/cdm_context_ref_impl.h"
 #include "third_party/blink/renderer/platform/media/web_content_decryption_module_session_impl.h"
 
-namespace media {
-
+namespace blink {
 namespace {
 const char kMediaEME[] = "Media.EME.";
 const char kDot[] = ".";
@@ -266,4 +265,4 @@
   return (session != sessions_.end()) ? session->second.get() : NULL;
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/cdm_session_adapter.h b/third_party/blink/renderer/platform/media/cdm_session_adapter.h
index 166c553..650ff09 100644
--- a/third_party/blink/renderer/platform/media/cdm_session_adapter.h
+++ b/third_party/blink/renderer/platform/media/cdm_session_adapter.h
@@ -27,10 +27,12 @@
 }  // namespace base
 
 namespace media {
-
-struct CdmConfig;
 class CdmContextRef;
 class CdmFactory;
+struct CdmConfig;
+}  // namespace media
+
+namespace blink {
 class WebContentDecryptionModuleSessionImpl;
 
 // Owns the CDM instance and makes calls from session objects to the CDM.
@@ -170,6 +172,6 @@
   base::WeakPtrFactory<CdmSessionAdapter> weak_ptr_factory_{this};
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_CDM_SESSION_ADAPTER_H_
diff --git a/third_party/blink/renderer/platform/media/interval_map_unittest.cc b/third_party/blink/renderer/platform/media/interval_map_unittest.cc
index 55bd538..092800b 100644
--- a/third_party/blink/renderer/platform/media/interval_map_unittest.cc
+++ b/third_party/blink/renderer/platform/media/interval_map_unittest.cc
@@ -13,7 +13,7 @@
 #include "media/base/test_random.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace {
+namespace blink {
 
 // Our tests only modifiy the interval map entries in [0..kTestSize).
 // We need this to be big enough to hit tricky corner cases, but small
@@ -111,9 +111,8 @@
  protected:
   media::TestRandom rnd_;
   SimpleIntervalMap truth_;
-  media::IntervalMap<int32_t, int32_t> testee_;
+  IntervalMap<int32_t, int32_t> testee_;
 };
-}
 
 TEST_F(IntervalMapTest, SimpleTest) {
   IncrementInterval(3, 7, 4);
@@ -272,3 +271,5 @@
     }
   }
 }
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/key_system_config_selector.cc b/third_party/blink/renderer/platform/media/key_system_config_selector.cc
index ca92f09d..cfe068e4 100644
--- a/third_party/blink/renderer/platform/media/key_system_config_selector.cc
+++ b/third_party/blink/renderer/platform/media/key_system_config_selector.cc
@@ -27,15 +27,17 @@
 #include "third_party/blink/public/web/modules/media/webmediaplayer_util.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 
-namespace media {
-
-using EmeFeatureRequirement =
-    blink::WebMediaKeySystemConfiguration::Requirement;
-using EmeEncryptionScheme =
-    blink::WebMediaKeySystemMediaCapability::EncryptionScheme;
-
+namespace blink {
 namespace {
 
+using ::media::EmeConfigRule;
+using ::media::EmeFeatureSupport;
+using ::media::EmeMediaType;
+using ::media::EmeSessionTypeSupport;
+using ::media::EncryptionScheme;
+using EmeFeatureRequirement = WebMediaKeySystemConfiguration::Requirement;
+using EmeEncryptionScheme = WebMediaKeySystemMediaCapability::EncryptionScheme;
+
 EmeConfigRule GetSessionTypeConfigRule(EmeSessionTypeSupport support) {
   switch (support) {
     case EmeSessionTypeSupport::INVALID:
@@ -1100,4 +1102,4 @@
   SelectConfigInternal(std::move(request));
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/key_system_config_selector_unittest.cc b/third_party/blink/renderer/platform/media/key_system_config_selector_unittest.cc
index faa68b90..22bc867 100644
--- a/third_party/blink/renderer/platform/media/key_system_config_selector_unittest.cc
+++ b/third_party/blink/renderer/platform/media/key_system_config_selector_unittest.cc
@@ -21,14 +21,14 @@
 #include "third_party/blink/public/platform/web_media_key_system_configuration.h"
 #include "third_party/blink/public/platform/web_string.h"
 
-namespace media {
-
+namespace blink {
 namespace {
 
-using blink::WebEncryptedMediaSessionType;
-using blink::WebMediaKeySystemConfiguration;
-using blink::WebMediaKeySystemMediaCapability;
-using blink::WebString;
+using ::media::EmeConfigRule;
+using ::media::EmeFeatureSupport;
+using ::media::EmeInitDataType;
+using ::media::EmeMediaType;
+using ::media::EmeSessionTypeSupport;
 using MediaKeysRequirement = WebMediaKeySystemConfiguration::Requirement;
 using EncryptionScheme = WebMediaKeySystemMediaCapability::EncryptionScheme;
 
@@ -1847,4 +1847,4 @@
   ASSERT_EQ("b", config_.label);
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/learning_experiment_helper.cc b/third_party/blink/renderer/platform/media/learning_experiment_helper.cc
index b571cf7..6c205a4 100644
--- a/third_party/blink/renderer/platform/media/learning_experiment_helper.cc
+++ b/third_party/blink/renderer/platform/media/learning_experiment_helper.cc
@@ -4,13 +4,12 @@
 
 #include "third_party/blink/public/platform/media/learning_experiment_helper.h"
 
-namespace media {
+namespace blink {
 
-using learning::FeatureDictionary;
-using learning::FeatureVector;
-using learning::LearningTask;
-using learning::LearningTaskController;
-using learning::TargetValue;
+using ::media::learning::FeatureDictionary;
+using ::media::learning::FeatureVector;
+using ::media::learning::LearningTaskController;
+using ::media::learning::TargetValue;
 
 LearningExperimentHelper::LearningExperimentHelper(
     std::unique_ptr<LearningTaskController> controller)
@@ -52,4 +51,4 @@
   observation_id_ = base::UnguessableToken::Null();
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/learning_experiment_helper_unittest.cc b/third_party/blink/renderer/platform/media/learning_experiment_helper_unittest.cc
index f973beb6..99efbd5 100644
--- a/third_party/blink/renderer/platform/media/learning_experiment_helper_unittest.cc
+++ b/third_party/blink/renderer/platform/media/learning_experiment_helper_unittest.cc
@@ -10,16 +10,16 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using media::learning::FeatureDictionary;
-using media::learning::FeatureValue;
-using media::learning::FeatureVector;
-using media::learning::LearningTask;
-using media::learning::LearningTaskController;
-using media::learning::ObservationCompletion;
-using media::learning::TargetValue;
-using testing::_;
+namespace blink {
 
-namespace media {
+using ::media::learning::FeatureDictionary;
+using ::media::learning::FeatureValue;
+using ::media::learning::FeatureVector;
+using ::media::learning::LearningTask;
+using ::media::learning::LearningTaskController;
+using ::media::learning::ObservationCompletion;
+using ::media::learning::TargetValue;
+using ::testing::_;
 
 class MockLearningTaskController : public LearningTaskController {
  public:
@@ -138,4 +138,4 @@
   helper_->CancelObservationIfNeeded();
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/lru_unittest.cc b/third_party/blink/renderer/platform/media/lru_unittest.cc
index ab45a3d..12c917b 100644
--- a/third_party/blink/renderer/platform/media/lru_unittest.cc
+++ b/third_party/blink/renderer/platform/media/lru_unittest.cc
@@ -12,14 +12,12 @@
 #include "media/base/test_random.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace blink {
+
 // Range of integer used in tests below.
 // We keep the integers small to get lots of re-use of integers.
 const int kTestIntRange = 16;
 
-namespace media {
-
-class LRUTest;
-
 class SimpleLRU {
  public:
   void Insert(int x) {
@@ -138,7 +136,7 @@
  protected:
   media::TestRandom rnd_;
   SimpleLRU truth_;
-  media::LRU<int> testee_;
+  LRU<int> testee_;
 };
 
 TEST_F(LRUTest, SimpleTest) {
@@ -234,4 +232,4 @@
   }
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/multi_buffer.cc b/third_party/blink/renderer/platform/media/multi_buffer.cc
index 4ff9dd6..c1ece9e 100644
--- a/third_party/blink/renderer/platform/media/multi_buffer.cc
+++ b/third_party/blink/renderer/platform/media/multi_buffer.cc
@@ -10,7 +10,7 @@
 #include "base/location.h"
 #include "base/logging.h"
 
-namespace media {
+namespace blink {
 
 // Prune 80 blocks per 30 seconds.
 // This means a full cache will go away in ~5 minutes.
@@ -569,4 +569,4 @@
   return i->second->AvailableBytes();
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/multi_buffer_data_source.cc b/third_party/blink/renderer/platform/media/multi_buffer_data_source.cc
index cd898d5..4b37d8de 100644
--- a/third_party/blink/renderer/platform/media/multi_buffer_data_source.cc
+++ b/third_party/blink/renderer/platform/media/multi_buffer_data_source.cc
@@ -18,6 +18,7 @@
 #include "third_party/blink/renderer/platform/media/multi_buffer_reader.h"
 #include "url/gurl.h"
 
+namespace blink {
 namespace {
 
 // Minimum preload buffer.
@@ -61,8 +62,6 @@
 
 }  // namespace
 
-namespace media {
-
 class MultiBufferDataSource::ReadOperation {
  public:
   ReadOperation() = delete;
@@ -779,4 +778,4 @@
   reader_->SetPreload(preload_high, preload);
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/multi_buffer_data_source_unittest.cc b/third_party/blink/renderer/platform/media/multi_buffer_data_source_unittest.cc
index 44c075b..b8d8b40 100644
--- a/third_party/blink/renderer/platform/media/multi_buffer_data_source_unittest.cc
+++ b/third_party/blink/renderer/platform/media/multi_buffer_data_source_unittest.cc
@@ -31,22 +31,15 @@
 #include "third_party/blink/renderer/platform/media/testing/mock_web_associated_url_loader.h"
 #include "third_party/blink/renderer/platform/media/testing/test_response_generator.h"
 
+namespace blink {
+
 using ::testing::_;
 using ::testing::Assign;
-using ::testing::DoAll;
 using ::testing::Invoke;
-using ::testing::InvokeWithoutArgs;
 using ::testing::InSequence;
 using ::testing::NiceMock;
 using ::testing::StrictMock;
 
-using blink::WebAssociatedURLLoader;
-using blink::WebString;
-using blink::WebURLResponse;
-
-namespace media {
-
-class TestResourceMultiBuffer;
 class TestMultiBufferDataProvider;
 
 std::set<TestMultiBufferDataProvider*> test_data_providers;
@@ -1884,4 +1877,4 @@
   Stop();
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/multi_buffer_reader.cc b/third_party/blink/renderer/platform/media/multi_buffer_reader.cc
index 448eaad..75bf62a0 100644
--- a/third_party/blink/renderer/platform/media/multi_buffer_reader.cc
+++ b/third_party/blink/renderer/platform/media/multi_buffer_reader.cc
@@ -14,7 +14,7 @@
 #include "base/single_thread_task_runner.h"
 #include "net/base/net_errors.h"
 
-namespace media {
+namespace blink {
 
 MultiBufferReader::MultiBufferReader(
     MultiBuffer* multibuffer,
@@ -249,4 +249,4 @@
   pinned_range_.end = end;
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/multi_buffer_reader.h b/third_party/blink/renderer/platform/media/multi_buffer_reader.h
index da07aa5..aacc162 100644
--- a/third_party/blink/renderer/platform/media/multi_buffer_reader.h
+++ b/third_party/blink/renderer/platform/media/multi_buffer_reader.h
@@ -17,7 +17,7 @@
 class SingleThreadTaskRunner;
 }
 
-namespace media {
+namespace blink {
 
 // Wrapper for MultiBuffer that offers a simple byte-reading
 // interface with prefetch.
@@ -191,6 +191,6 @@
   base::WeakPtrFactory<MultiBufferReader> weak_factory_{this};
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_MULTI_BUFFER_READER_H_
diff --git a/third_party/blink/renderer/platform/media/multi_buffer_unittest.cc b/third_party/blink/renderer/platform/media/multi_buffer_unittest.cc
index e3fa05d..24c9a337 100644
--- a/third_party/blink/renderer/platform/media/multi_buffer_unittest.cc
+++ b/third_party/blink/renderer/platform/media/multi_buffer_unittest.cc
@@ -23,16 +23,14 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/media/multi_buffer_reader.h"
 
+namespace blink {
+namespace {
+class FakeMultiBufferDataProvider;
+
 const int kBlockSizeShift = 8;
 const size_t kBlockSize = 1UL << kBlockSizeShift;
 
-namespace media {
-
-class FakeMultiBufferDataProvider;
-
-namespace {
 std::vector<FakeMultiBufferDataProvider*> writers;
-}  // namespace
 
 class FakeMultiBufferDataProvider : public MultiBuffer::DataProvider {
  public:
@@ -129,6 +127,8 @@
   media::TestRandom* rnd_;
 };
 
+}  // namespace
+
 class TestMultiBuffer : public MultiBuffer {
  public:
   explicit TestMultiBuffer(int32_t shift,
@@ -606,4 +606,4 @@
   }
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/new_session_cdm_result_promise.cc b/third_party/blink/renderer/platform/media/new_session_cdm_result_promise.cc
index fe85c44..7783e43 100644
--- a/third_party/blink/renderer/platform/media/new_session_cdm_result_promise.cc
+++ b/third_party/blink/renderer/platform/media/new_session_cdm_result_promise.cc
@@ -10,8 +10,7 @@
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/renderer/platform/media/cdm_result_promise_helper.h"
 
-namespace media {
-
+namespace blink {
 namespace {
 
 const char kTimeUMAPrefix[] = "TimeTo.";
@@ -106,4 +105,4 @@
                                     blink::WebString::FromUTF8(error_message));
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/new_session_cdm_result_promise.h b/third_party/blink/renderer/platform/media/new_session_cdm_result_promise.h
index f78f42d..d9ee42d 100644
--- a/third_party/blink/renderer/platform/media/new_session_cdm_result_promise.h
+++ b/third_party/blink/renderer/platform/media/new_session_cdm_result_promise.h
@@ -15,7 +15,7 @@
 #include "third_party/blink/public/platform/web_content_decryption_module_result.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 
-namespace media {
+namespace blink {
 
 enum class SessionInitStatus {
   // Unable to determine the status.
@@ -79,6 +79,6 @@
   base::TimeTicks creation_time_;
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_NEW_SESSION_CDM_RESULT_PROMISE_H_
diff --git a/third_party/blink/renderer/platform/media/power_status_helper.cc b/third_party/blink/renderer/platform/media/power_status_helper.cc
index 42d14fc..4aa5009 100644
--- a/third_party/blink/renderer/platform/media/power_status_helper.cc
+++ b/third_party/blink/renderer/platform/media/power_status_helper.cc
@@ -12,7 +12,7 @@
 #include "media/base/pipeline_metadata.h"
 #include "services/device/public/mojom/battery_status.mojom.h"
 
-namespace media {
+namespace blink {
 namespace {
 
 using ::device::mojom::BatteryStatusPtr;
@@ -307,4 +307,4 @@
       &PowerStatusHelper::OnBatteryStatus, base::Unretained(this)));
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/power_status_helper_unittest.cc b/third_party/blink/renderer/platform/media/power_status_helper_unittest.cc
index 3a7d826..af97b2a 100644
--- a/third_party/blink/renderer/platform/media/power_status_helper_unittest.cc
+++ b/third_party/blink/renderer/platform/media/power_status_helper_unittest.cc
@@ -18,17 +18,10 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace media {
+namespace blink {
 
-using ::testing::_;
-using ::testing::AnyNumber;
 using ::testing::Bool;
 using ::testing::Combine;
-using ::testing::Eq;
-using ::testing::Gt;
-using ::testing::Lt;
-using ::testing::ResultOf;
-using ::testing::Return;
 using ::testing::Values;
 
 class PowerStatusHelperTest : public testing::Test {
@@ -444,4 +437,4 @@
             Values(PowerStatusHelper::Bits::kFullScreenNo,
                    PowerStatusHelper::Bits::kFullScreenYes)));
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/remote_playback_client_wrapper_impl.cc b/third_party/blink/renderer/platform/media/remote_playback_client_wrapper_impl.cc
index 19f5733..5724d224 100644
--- a/third_party/blink/renderer/platform/media/remote_playback_client_wrapper_impl.cc
+++ b/third_party/blink/renderer/platform/media/remote_playback_client_wrapper_impl.cc
@@ -8,7 +8,7 @@
 #include "third_party/blink/public/platform/web_media_player_client.h"
 #include "third_party/blink/public/platform/web_string.h"
 
-namespace media {
+namespace blink {
 
 RemotePlaybackClientWrapperImpl::RemotePlaybackClientWrapperImpl(
     blink::WebMediaPlayerClient* client)
@@ -27,4 +27,4 @@
   return remote_playback_client_->GetPresentationId().Ascii();
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc
index 1b46574..b5f4b15 100644
--- a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc
+++ b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc
@@ -29,13 +29,7 @@
 #include "third_party/blink/public/web/web_associated_url_loader.h"
 #include "third_party/blink/renderer/platform/media/cache_util.h"
 
-using blink::WebAssociatedURLLoader;
-using blink::WebString;
-using blink::WebURLError;
-using blink::WebURLRequest;
-using blink::WebURLResponse;
-
-namespace media {
+namespace blink {
 
 // The number of milliseconds to wait before retrying a failed load.
 const int kLoaderFailedRetryDelayMs = 250;
@@ -579,4 +573,4 @@
   return true;
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.h b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.h
index 0266865..14a3920 100644
--- a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.h
+++ b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.h
@@ -27,9 +27,6 @@
 
 namespace blink {
 class WebAssociatedURLLoader;
-}  // namespace blink
-
-namespace media {
 
 class PLATFORM_EXPORT ResourceMultiBufferDataProvider
     : public MultiBuffer::DataProvider,
@@ -70,7 +67,6 @@
  protected:
   friend class MultiBufferDataSourceTest;
   friend class ResourceMultiBufferDataProviderTest;
-  friend class MockBufferedDataSource;
 
   // Callback used when we're asked to fetch data after the end of the file.
   void Terminate();
@@ -135,6 +131,6 @@
   base::WeakPtrFactory<ResourceMultiBufferDataProvider> weak_factory_{this};
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_RESOURCE_MULTI_BUFFER_DATA_PROVIDER_H_
diff --git a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider_unittest.cc b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider_unittest.cc
index 78af7b8..1a9f727 100644
--- a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider_unittest.cc
+++ b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider_unittest.cc
@@ -32,19 +32,13 @@
 #include "third_party/blink/renderer/platform/media/testing/mock_resource_fetch_context.h"
 #include "third_party/blink/renderer/platform/media/testing/mock_web_associated_url_loader.h"
 
+namespace blink {
+
 using ::testing::_;
-using ::testing::InSequence;
 using ::testing::Invoke;
 using ::testing::NiceMock;
-using ::testing::Return;
 using ::testing::Truly;
 
-using blink::WebString;
-using blink::WebURLError;
-using blink::WebURLResponse;
-
-namespace media {
-
 const char kHttpUrl[] = "http://test";
 const char kHttpsUrl[] = "https://test";
 const char kHttpRedirect[] = "http://test/ing";
@@ -388,4 +382,4 @@
   }
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/smoothness_helper.cc b/third_party/blink/renderer/platform/media/smoothness_helper.cc
index 77ea759..f8ede690 100644
--- a/third_party/blink/renderer/platform/media/smoothness_helper.cc
+++ b/third_party/blink/renderer/platform/media/smoothness_helper.cc
@@ -10,7 +10,13 @@
 #include "media/learning/common/learning_task_controller.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
+namespace blink {
 namespace {
+
+using ::media::learning::FeatureVector;
+using ::media::learning::LearningTaskController;
+using ::media::learning::TargetValue;
+
 static constexpr base::TimeDelta kSegmentSize =
     base::TimeDelta::FromSeconds(5);
 
@@ -20,13 +26,8 @@
 
 // Max proportion of dropped frames in a window before we call it "not smooth".
 static constexpr float kMaxDroppedFramesPerWindow = 0.2;
-}
 
-namespace media {
-
-using learning::FeatureVector;
-using learning::LearningTaskController;
-using learning::TargetValue;
+}  // namespace
 
 // Monitor smoothness during a playback, and call back on each window.
 class SmoothnessWindowMonitor {
@@ -71,7 +72,7 @@
   int64_t segment_dropped_frames_;
 };
 
-SmoothnessHelper::SmoothnessHelper(const learning::FeatureVector& features)
+SmoothnessHelper::SmoothnessHelper(const FeatureVector& features)
     : features_(features) {}
 
 SmoothnessHelper::~SmoothnessHelper() = default;
@@ -236,4 +237,4 @@
   return kSegmentSize;
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/smoothness_helper_unittest.cc b/third_party/blink/renderer/platform/media/smoothness_helper_unittest.cc
index 3e00bed4..c693c2f 100644
--- a/third_party/blink/renderer/platform/media/smoothness_helper_unittest.cc
+++ b/third_party/blink/renderer/platform/media/smoothness_helper_unittest.cc
@@ -11,22 +11,17 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace media {
+namespace blink {
 
-using learning::FeatureValue;
-using learning::FeatureVector;
-using learning::LearningTask;
-using learning::LearningTaskController;
-using learning::ObservationCompletion;
-using learning::TargetValue;
-
-using testing::_;
-using testing::AnyNumber;
-using testing::Eq;
-using testing::Gt;
-using testing::Lt;
-using testing::ResultOf;
-using testing::Return;
+using ::media::learning::FeatureValue;
+using ::media::learning::FeatureVector;
+using ::media::learning::LearningTask;
+using ::media::learning::LearningTaskController;
+using ::media::learning::ObservationCompletion;
+using ::media::learning::TargetValue;
+using ::testing::_;
+using ::testing::ResultOf;
+using ::testing::Return;
 
 // Helper for EXPECT_CALL argument matching on Optional<TargetValue>.  Applies
 // matcher |m| to the TargetValue as a double.  For example:
@@ -208,4 +203,4 @@
   helper_->NotifyNNR();
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/testing/mock_resource_fetch_context.cc b/third_party/blink/renderer/platform/media/testing/mock_resource_fetch_context.cc
index 6fc01d4a..901f5e94 100644
--- a/third_party/blink/renderer/platform/media/testing/mock_resource_fetch_context.cc
+++ b/third_party/blink/renderer/platform/media/testing/mock_resource_fetch_context.cc
@@ -4,10 +4,10 @@
 
 #include "third_party/blink/renderer/platform/media/testing/mock_resource_fetch_context.h"
 
-namespace media {
+namespace blink {
 
 MockResourceFetchContext::MockResourceFetchContext() = default;
 
 MockResourceFetchContext::~MockResourceFetchContext() = default;
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/testing/mock_resource_fetch_context.h b/third_party/blink/renderer/platform/media/testing/mock_resource_fetch_context.h
index 58e6ce2..23b0c69 100644
--- a/third_party/blink/renderer/platform/media/testing/mock_resource_fetch_context.h
+++ b/third_party/blink/renderer/platform/media/testing/mock_resource_fetch_context.h
@@ -8,7 +8,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/platform/media/resource_fetch_context.h"
 
-namespace media {
+namespace blink {
 
 class MockResourceFetchContext : public ResourceFetchContext {
  public:
@@ -22,6 +22,6 @@
                    const blink::WebAssociatedURLLoaderOptions&));
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_TESTING_MOCK_RESOURCE_FETCH_CONTEXT_H_
diff --git a/third_party/blink/renderer/platform/media/testing/mock_web_associated_url_loader.cc b/third_party/blink/renderer/platform/media/testing/mock_web_associated_url_loader.cc
index 61fb02ef..b2cab13 100644
--- a/third_party/blink/renderer/platform/media/testing/mock_web_associated_url_loader.cc
+++ b/third_party/blink/renderer/platform/media/testing/mock_web_associated_url_loader.cc
@@ -9,10 +9,10 @@
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/public/platform/web_url_response.h"
 
-namespace media {
+namespace blink {
 
 MockWebAssociatedURLLoader::MockWebAssociatedURLLoader() = default;
 
 MockWebAssociatedURLLoader::~MockWebAssociatedURLLoader() = default;
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/testing/mock_web_associated_url_loader.h b/third_party/blink/renderer/platform/media/testing/mock_web_associated_url_loader.h
index f3b028e..dd5bfb8 100644
--- a/third_party/blink/renderer/platform/media/testing/mock_web_associated_url_loader.h
+++ b/third_party/blink/renderer/platform/media/testing/mock_web_associated_url_loader.h
@@ -9,7 +9,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/web/web_associated_url_loader.h"
 
-namespace media {
+namespace blink {
 
 class MockWebAssociatedURLLoader : public blink::WebAssociatedURLLoader {
  public:
@@ -28,6 +28,6 @@
                void(base::SingleThreadTaskRunner* task_runner));
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_TESTING_MOCK_WEB_ASSOCIATED_URL_LOADER_H_
diff --git a/third_party/blink/renderer/platform/media/testing/test_response_generator.cc b/third_party/blink/renderer/platform/media/testing/test_response_generator.cc
index 67de9e8..107bae5 100644
--- a/third_party/blink/renderer/platform/media/testing/test_response_generator.cc
+++ b/third_party/blink/renderer/platform/media/testing/test_response_generator.cc
@@ -10,12 +10,7 @@
 #include "net/base/net_errors.h"
 #include "third_party/blink/public/platform/web_string.h"
 
-using blink::WebString;
-using blink::WebURL;
-using blink::WebURLError;
-using blink::WebURLResponse;
-
-namespace media {
+namespace blink {
 
 TestResponseGenerator::TestResponseGenerator(const GURL& gurl,
                                              int64_t content_length)
@@ -109,4 +104,4 @@
   return response;
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/testing/test_response_generator.h b/third_party/blink/renderer/platform/media/testing/test_response_generator.h
index 5c871f9..c2ca2523 100644
--- a/third_party/blink/renderer/platform/media/testing/test_response_generator.h
+++ b/third_party/blink/renderer/platform/media/testing/test_response_generator.h
@@ -11,7 +11,7 @@
 #include "third_party/blink/public/platform/web_url_response.h"
 #include "url/gurl.h"
 
-namespace media {
+namespace blink {
 
 // Generates WebURLErrors and WebURLResponses suitable for testing purposes.
 class TestResponseGenerator {
@@ -77,6 +77,6 @@
   int64_t content_length_;
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_TESTING_TEST_RESPONSE_GENERATOR_H_
diff --git a/third_party/blink/renderer/platform/media/text_track_impl.cc b/third_party/blink/renderer/platform/media/text_track_impl.cc
index c7f8e33..26939631 100644
--- a/third_party/blink/renderer/platform/media/text_track_impl.cc
+++ b/third_party/blink/renderer/platform/media/text_track_impl.cc
@@ -14,7 +14,7 @@
 #include "third_party/blink/public/platform/web_media_player_client.h"
 #include "third_party/blink/renderer/platform/media/web_inband_text_track_impl.h"
 
-namespace media {
+namespace blink {
 
 TextTrackImpl::TextTrackImpl(
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
@@ -63,4 +63,4 @@
     client->RemoveTextTrack(text_track.get());
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/text_track_impl.h b/third_party/blink/renderer/platform/media/text_track_impl.h
index 5214cebb..14895bd 100644
--- a/third_party/blink/renderer/platform/media/text_track_impl.h
+++ b/third_party/blink/renderer/platform/media/text_track_impl.h
@@ -16,12 +16,8 @@
 }
 
 namespace blink {
-class WebMediaPlayerClient;
-}
-
-namespace media {
-
 class WebInbandTextTrackImpl;
+class WebMediaPlayerClient;
 
 class PLATFORM_EXPORT TextTrackImpl : public media::TextTrack {
  public:
@@ -56,6 +52,6 @@
   std::unique_ptr<WebInbandTextTrackImpl> text_track_;
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_TEXT_TRACK_IMPL_H_
diff --git a/third_party/blink/renderer/platform/media/url_index.cc b/third_party/blink/renderer/platform/media/url_index.cc
index f0e9b76f8..6d1cc9e 100644
--- a/third_party/blink/renderer/platform/media/url_index.cc
+++ b/third_party/blink/renderer/platform/media/url_index.cc
@@ -15,7 +15,7 @@
 #include "media/base/media_switches.h"
 #include "third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.h"
 
-namespace media {
+namespace blink {
 
 const int kBlockSizeShift = 15;  // 1<<15 == 32kb
 const int kUrlMappingTimeoutSeconds = 300;
@@ -337,4 +337,4 @@
   return iter->second;
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/url_index_unittest.cc b/third_party/blink/renderer/platform/media/url_index_unittest.cc
index 6bc88b4f..094f1cd 100644
--- a/third_party/blink/renderer/platform/media/url_index_unittest.cc
+++ b/third_party/blink/renderer/platform/media/url_index_unittest.cc
@@ -17,7 +17,7 @@
 #include "media/base/media_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace media {
+namespace blink {
 
 class UrlIndexTest : public testing::Test {
  public:
@@ -176,4 +176,4 @@
   EXPECT_NE(url_data, url_index_.GetByUrl(url, cors, UrlIndex::kCacheDisabled));
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/video_decode_stats_reporter.cc b/third_party/blink/renderer/platform/media/video_decode_stats_reporter.cc
index d0fa7a62..25a1a1cd 100644
--- a/third_party/blink/renderer/platform/media/video_decode_stats_reporter.cc
+++ b/third_party/blink/renderer/platform/media/video_decode_stats_reporter.cc
@@ -12,7 +12,7 @@
 #include "media/capabilities/bucket_utility.h"
 #include "media/mojo/mojom/media_types.mojom.h"
 
-namespace media {
+namespace blink {
 
 VideoDecodeStatsReporter::VideoDecodeStatsReporter(
     mojo::PendingRemote<media::mojom::VideoDecodeStatsRecorder> recorder_remote,
@@ -329,4 +329,4 @@
   recorder_remote_->UpdateRecord(std::move(targets));
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/video_decode_stats_reporter.h b/third_party/blink/renderer/platform/media/video_decode_stats_reporter.h
index ac1e06b98..54c7078 100644
--- a/third_party/blink/renderer/platform/media/video_decode_stats_reporter.h
+++ b/third_party/blink/renderer/platform/media/video_decode_stats_reporter.h
@@ -24,7 +24,7 @@
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "ui/gfx/geometry/size.h"
 
-namespace media {
+namespace blink {
 
 // Reports on playback smoothness and for a given video codec profile, natural
 // size, and fps. When these properties change the current report will be
@@ -240,6 +240,6 @@
   bool is_ipc_connected_ = true;
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_VIDEO_DECODE_STATS_REPORTER_H_
diff --git a/third_party/blink/renderer/platform/media/video_decode_stats_reporter_unittest.cc b/third_party/blink/renderer/platform/media/video_decode_stats_reporter_unittest.cc
index a91f0e7..b0250a13 100644
--- a/third_party/blink/renderer/platform/media/video_decode_stats_reporter_unittest.cc
+++ b/third_party/blink/renderer/platform/media/video_decode_stats_reporter_unittest.cc
@@ -27,12 +27,12 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/rect.h"
 
+namespace blink {
+
 using ::testing::Invoke;
 using ::testing::Return;
 using ::testing::_;
 
-namespace media {
-
 const media::VideoCodecProfile kDefaultProfile = media::VP9PROFILE_PROFILE0;
 const int kDefaultHeight = 480;
 const int kDefaultWidth = 640;
@@ -932,4 +932,4 @@
   FastForward(kRecordingInterval);
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/video_frame_compositor.cc b/third_party/blink/renderer/platform/media/video_frame_compositor.cc
index dc92202..ad1bb0e 100644
--- a/third_party/blink/renderer/platform/media/video_frame_compositor.cc
+++ b/third_party/blink/renderer/platform/media/video_frame_compositor.cc
@@ -17,9 +17,9 @@
 #include "media/base/video_frame.h"
 #include "third_party/blink/public/platform/web_video_frame_submitter.h"
 
-namespace media {
+namespace blink {
 
-using RenderingMode = VideoRendererSink::RenderCallback::RenderingMode;
+using RenderingMode = ::media::VideoRendererSink::RenderCallback::RenderingMode;
 
 // Amount of time to wait between UpdateCurrentFrame() callbacks before starting
 // background rendering to keep the Render() callbacks moving.
@@ -462,4 +462,4 @@
   return new_frame || had_new_background_frame;
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/video_frame_compositor_unittest.cc b/third_party/blink/renderer/platform/media/video_frame_compositor_unittest.cc
index c8e300a2..b63afd3 100644
--- a/third_party/blink/renderer/platform/media/video_frame_compositor_unittest.cc
+++ b/third_party/blink/renderer/platform/media/video_frame_compositor_unittest.cc
@@ -19,15 +19,15 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/web_video_frame_submitter.h"
 
-using base::test::RunClosure;
-using testing::_;
-using testing::AnyNumber;
-using testing::DoAll;
-using testing::Eq;
-using testing::Return;
-using testing::StrictMock;
+namespace blink {
 
-namespace media {
+using ::base::test::RunClosure;
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::DoAll;
+using ::testing::Eq;
+using ::testing::Return;
+using ::testing::StrictMock;
 
 using RenderingMode = ::media::VideoRendererSink::RenderCallback::RenderingMode;
 
@@ -491,4 +491,4 @@
             viz::BeginFrameArgs::MinInterval());
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/watch_time_reporter_unittest.cc b/third_party/blink/renderer/platform/media/watch_time_reporter_unittest.cc
index 25f76e2..f09c75d 100644
--- a/third_party/blink/renderer/platform/media/watch_time_reporter_unittest.cc
+++ b/third_party/blink/renderer/platform/media/watch_time_reporter_unittest.cc
@@ -25,13 +25,14 @@
 #include "third_party/blink/public/common/media/watch_time_reporter.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 
-namespace media {
+namespace blink {
+
+using ::media::WatchTimeKey;
+using ::testing::_;
 
 constexpr gfx::Size kSizeTooSmall = gfx::Size(101, 101);
 constexpr gfx::Size kSizeJustRight = gfx::Size(201, 201);
 
-using testing::_;
-
 #define EXPECT_WATCH_TIME(key, value)                                          \
   do {                                                                         \
     EXPECT_CALL(                                                               \
@@ -2395,4 +2396,4 @@
                              std::make_tuple(true, true),
                          }));
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/web_content_decryption_module_access_impl.cc b/third_party/blink/renderer/platform/media/web_content_decryption_module_access_impl.cc
index 2ee33474..767f8518 100644
--- a/third_party/blink/renderer/platform/media/web_content_decryption_module_access_impl.cc
+++ b/third_party/blink/renderer/platform/media/web_content_decryption_module_access_impl.cc
@@ -12,7 +12,7 @@
 #include "base/single_thread_task_runner.h"
 #include "third_party/blink/public/platform/media/web_encrypted_media_client_impl.h"
 
-namespace media {
+namespace blink {
 
 // The caller owns the created cdm (passed back using |result|).
 static void CreateCdm(
@@ -94,4 +94,4 @@
   return cdm_config_.use_hw_secure_codecs;
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/web_content_decryption_module_access_impl.h b/third_party/blink/renderer/platform/media/web_content_decryption_module_access_impl.h
index a771629a..19b6d0e51 100644
--- a/third_party/blink/renderer/platform/media/web_content_decryption_module_access_impl.h
+++ b/third_party/blink/renderer/platform/media/web_content_decryption_module_access_impl.h
@@ -16,8 +16,7 @@
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 
-namespace media {
-
+namespace blink {
 class WebEncryptedMediaClientImpl;
 
 class PLATFORM_EXPORT WebContentDecryptionModuleAccessImpl
@@ -63,6 +62,6 @@
   base::WeakPtr<WebEncryptedMediaClientImpl> client_;
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_WEB_CONTENT_DECRYPTION_MODULE_ACCESS_IMPL_H_
diff --git a/third_party/blink/renderer/platform/media/web_content_decryption_module_impl.cc b/third_party/blink/renderer/platform/media/web_content_decryption_module_impl.cc
index c89814d..ed555ae 100644
--- a/third_party/blink/renderer/platform/media/web_content_decryption_module_impl.cc
+++ b/third_party/blink/renderer/platform/media/web_content_decryption_module_impl.cc
@@ -24,8 +24,7 @@
 #include "third_party/blink/renderer/platform/media/web_content_decryption_module_session_impl.h"
 #include "url/origin.h"
 
-namespace media {
-
+namespace blink {
 namespace {
 
 const char kCreateSessionSessionTypeUMAName[] = "CreateSession.SessionType";
@@ -33,7 +32,7 @@
 const char kGetStatusForPolicyUMAName[] = "GetStatusForPolicy";
 
 bool ConvertHdcpVersion(const blink::WebString& hdcp_version_string,
-                        HdcpVersion* hdcp_version) {
+                        media::HdcpVersion* hdcp_version) {
   if (!hdcp_version_string.ContainsOnlyASCII())
     return false;
 
@@ -42,25 +41,25 @@
   // The strings are specified in the explainer doc:
   // https://github.com/WICG/hdcp-detection/blob/master/explainer.md
   if (hdcp_version_ascii.empty())
-    *hdcp_version = HdcpVersion::kHdcpVersionNone;
+    *hdcp_version = media::HdcpVersion::kHdcpVersionNone;
   else if (hdcp_version_ascii == "1.0")
-    *hdcp_version = HdcpVersion::kHdcpVersion1_0;
+    *hdcp_version = media::HdcpVersion::kHdcpVersion1_0;
   else if (hdcp_version_ascii == "1.1")
-    *hdcp_version = HdcpVersion::kHdcpVersion1_1;
+    *hdcp_version = media::HdcpVersion::kHdcpVersion1_1;
   else if (hdcp_version_ascii == "1.2")
-    *hdcp_version = HdcpVersion::kHdcpVersion1_2;
+    *hdcp_version = media::HdcpVersion::kHdcpVersion1_2;
   else if (hdcp_version_ascii == "1.3")
-    *hdcp_version = HdcpVersion::kHdcpVersion1_3;
+    *hdcp_version = media::HdcpVersion::kHdcpVersion1_3;
   else if (hdcp_version_ascii == "1.4")
-    *hdcp_version = HdcpVersion::kHdcpVersion1_4;
+    *hdcp_version = media::HdcpVersion::kHdcpVersion1_4;
   else if (hdcp_version_ascii == "2.0")
-    *hdcp_version = HdcpVersion::kHdcpVersion2_0;
+    *hdcp_version = media::HdcpVersion::kHdcpVersion2_0;
   else if (hdcp_version_ascii == "2.1")
-    *hdcp_version = HdcpVersion::kHdcpVersion2_1;
+    *hdcp_version = media::HdcpVersion::kHdcpVersion2_1;
   else if (hdcp_version_ascii == "2.2")
-    *hdcp_version = HdcpVersion::kHdcpVersion2_2;
+    *hdcp_version = media::HdcpVersion::kHdcpVersion2_2;
   else if (hdcp_version_ascii == "2.3")
-    *hdcp_version = HdcpVersion::kHdcpVersion2_3;
+    *hdcp_version = media::HdcpVersion::kHdcpVersion2_3;
   else
     return false;
 
@@ -144,7 +143,7 @@
 void WebContentDecryptionModuleImpl::GetStatusForPolicy(
     const blink::WebString& min_hdcp_version_string,
     blink::WebContentDecryptionModuleResult result) {
-  HdcpVersion min_hdcp_version;
+  media::HdcpVersion min_hdcp_version;
   if (!ConvertHdcpVersion(min_hdcp_version_string, &min_hdcp_version)) {
     result.CompleteWithError(
         blink::kWebContentDecryptionModuleExceptionTypeError, 0,
@@ -172,4 +171,4 @@
   return adapter_->GetCdmConfig();
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/web_content_decryption_module_impl.h b/third_party/blink/renderer/platform/media/web_content_decryption_module_impl.h
index 05979ca..3797a1e 100644
--- a/third_party/blink/renderer/platform/media/web_content_decryption_module_impl.h
+++ b/third_party/blink/renderer/platform/media/web_content_decryption_module_impl.h
@@ -17,16 +17,15 @@
 #include "third_party/blink/public/platform/web_content_decryption_module.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 
-namespace blink {
-class WebSecurityOrigin;
-}
-
 namespace media {
-
-struct CdmConfig;
 class CdmContextRef;
 class CdmFactory;
+struct CdmConfig;
+}  // namespace media
+
+namespace blink {
 class CdmSessionAdapter;
+class WebSecurityOrigin;
 
 using WebCdmCreatedCB =
     base::OnceCallback<void(blink::WebContentDecryptionModule* cdm,
@@ -80,6 +79,6 @@
   return static_cast<WebContentDecryptionModuleImpl*>(cdm);
 }
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_WEB_CONTENT_DECRYPTION_MODULE_IMPL_H_
diff --git a/third_party/blink/renderer/platform/media/web_content_decryption_module_session_impl.cc b/third_party/blink/renderer/platform/media/web_content_decryption_module_session_impl.cc
index 5a5bf44..9f6cb4f 100644
--- a/third_party/blink/renderer/platform/media/web_content_decryption_module_session_impl.cc
+++ b/third_party/blink/renderer/platform/media/web_content_decryption_module_session_impl.cc
@@ -32,8 +32,7 @@
 #include "third_party/blink/renderer/platform/media/cdm_result_promise_helper.h"
 #include "third_party/blink/renderer/platform/media/cdm_session_adapter.h"
 
-namespace media {
-
+namespace blink {
 namespace {
 
 const char kCloseSessionUMAName[] = "CloseSession";
@@ -43,22 +42,22 @@
 const char kUpdateSessionUMAName[] = "UpdateSession";
 const char kKeyStatusSystemCodeUMAName[] = "KeyStatusSystemCode";
 
-CdmSessionType ConvertSessionType(
+media::CdmSessionType ConvertSessionType(
     blink::WebEncryptedMediaSessionType session_type) {
   switch (session_type) {
     case blink::WebEncryptedMediaSessionType::kTemporary:
-      return CdmSessionType::kTemporary;
+      return media::CdmSessionType::kTemporary;
     case blink::WebEncryptedMediaSessionType::kPersistentLicense:
-      return CdmSessionType::kPersistentLicense;
+      return media::CdmSessionType::kPersistentLicense;
     case blink::WebEncryptedMediaSessionType::kUnknown:
       break;
   }
 
   NOTREACHED();
-  return CdmSessionType::kTemporary;
+  return media::CdmSessionType::kTemporary;
 }
 
-bool SanitizeInitData(EmeInitDataType init_data_type,
+bool SanitizeInitData(media::EmeInitDataType init_data_type,
                       const unsigned char* init_data,
                       size_t init_data_length,
                       std::vector<uint8_t>* sanitized_init_data,
@@ -70,7 +69,7 @@
   }
 
   switch (init_data_type) {
-    case EmeInitDataType::WEBM:
+    case media::EmeInitDataType::WEBM:
       // |init_data| for WebM is a single key.
       if (init_data_length > media::limits::kMaxKeyIdLength) {
         error_message->assign("Initialization data for WebM is too long.");
@@ -79,7 +78,7 @@
       sanitized_init_data->assign(init_data, init_data + init_data_length);
       return true;
 
-    case EmeInitDataType::CENC:
+    case media::EmeInitDataType::CENC:
       sanitized_init_data->assign(init_data, init_data + init_data_length);
       if (!media::ValidatePsshInput(*sanitized_init_data)) {
         error_message->assign("Initialization data for CENC is incorrect.");
@@ -87,7 +86,7 @@
       }
       return true;
 
-    case EmeInitDataType::KEYIDS: {
+    case media::EmeInitDataType::KEYIDS: {
       // Extract the keys and then rebuild the message. This ensures that any
       // extra data in the provided JSON is dropped.
       std::string init_data_string(init_data, init_data + init_data_length);
@@ -108,7 +107,7 @@
       return true;
     }
 
-    case EmeInitDataType::UNKNOWN:
+    case media::EmeInitDataType::UNKNOWN:
       break;
   }
 
@@ -157,7 +156,7 @@
   if (media::IsClearKey(key_system) || media::IsExternalClearKey(key_system)) {
     std::string key_string(response, response + response_length);
     media::KeyIdAndKeyPairs keys;
-    auto session_type = CdmSessionType::kTemporary;
+    auto session_type = media::CdmSessionType::kTemporary;
     if (!ExtractKeysFromJWKSet(key_string, &keys, &session_type))
       return false;
 
@@ -226,7 +225,7 @@
 }
 
 void WebContentDecryptionModuleSessionImpl::InitializeNewSession(
-    EmeInitDataType eme_init_data_type,
+    media::EmeInitDataType eme_init_data_type,
     const unsigned char* init_data,
     size_t init_data_length,
     blink::WebContentDecryptionModuleResult result) {
@@ -311,7 +310,7 @@
   DCHECK(!session_id.IsEmpty());
   DCHECK(session_id_.empty());
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(session_type_ == CdmSessionType::kPersistentLicense);
+  DCHECK(session_type_ == media::CdmSessionType::kPersistentLicense);
 
   // From https://w3c.github.io/encrypted-media/#load.
   // 8.1 Let sanitized session ID be a validated and/or sanitized version of
@@ -407,7 +406,7 @@
 }
 
 void WebContentDecryptionModuleSessionImpl::OnSessionMessage(
-    CdmMessageType message_type,
+    media::CdmMessageType message_type,
     const std::vector<uint8_t>& message) {
   DCHECK(client_) << "Client not set before message event";
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -476,4 +475,4 @@
           : SessionInitStatus::SESSION_ALREADY_EXISTS;
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/web_content_decryption_module_session_impl.h b/third_party/blink/renderer/platform/media/web_content_decryption_module_session_impl.h
index 4974aa9..694b1bf 100644
--- a/third_party/blink/renderer/platform/media/web_content_decryption_module_session_impl.h
+++ b/third_party/blink/renderer/platform/media/web_content_decryption_module_session_impl.h
@@ -21,8 +21,7 @@
 #include "third_party/blink/renderer/platform/media/new_session_cdm_result_promise.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 
-namespace media {
-
+namespace blink {
 class CdmSessionAdapter;
 
 class PLATFORM_EXPORT WebContentDecryptionModuleSessionImpl
@@ -101,6 +100,6 @@
       this};
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_WEB_CONTENT_DECRYPTION_MODULE_SESSION_IMPL_H_
diff --git a/third_party/blink/renderer/platform/media/web_encrypted_media_client_impl.cc b/third_party/blink/renderer/platform/media/web_encrypted_media_client_impl.cc
index 50d48e7..bc50a8511 100644
--- a/third_party/blink/renderer/platform/media/web_encrypted_media_client_impl.cc
+++ b/third_party/blink/renderer/platform/media/web_encrypted_media_client_impl.cc
@@ -21,8 +21,7 @@
 #include "third_party/blink/renderer/platform/media/web_content_decryption_module_access_impl.h"
 #include "third_party/blink/renderer/platform/media/web_content_decryption_module_impl.h"
 
-namespace media {
-
+namespace blink {
 namespace {
 
 // Used to name UMAs in Reporter.
@@ -187,4 +186,4 @@
   return reporter.get();
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/web_inband_text_track_impl.cc b/third_party/blink/renderer/platform/media/web_inband_text_track_impl.cc
index 641f9e4..d5d79bd 100644
--- a/third_party/blink/renderer/platform/media/web_inband_text_track_impl.cc
+++ b/third_party/blink/renderer/platform/media/web_inband_text_track_impl.cc
@@ -6,7 +6,7 @@
 
 #include "base/check.h"
 
-namespace media {
+namespace blink {
 
 WebInbandTextTrackImpl::WebInbandTextTrackImpl(Kind kind,
                                                const blink::WebString& label,
@@ -47,4 +47,4 @@
   return id_;
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/web_inband_text_track_impl.h b/third_party/blink/renderer/platform/media/web_inband_text_track_impl.h
index 2170773..d697458 100644
--- a/third_party/blink/renderer/platform/media/web_inband_text_track_impl.h
+++ b/third_party/blink/renderer/platform/media/web_inband_text_track_impl.h
@@ -9,7 +9,7 @@
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 
-namespace media {
+namespace blink {
 
 class PLATFORM_EXPORT WebInbandTextTrackImpl
     : public blink::WebInbandTextTrack {
@@ -39,6 +39,6 @@
   blink::WebString id_;
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_WEB_INBAND_TEXT_TRACK_IMPL_H_
diff --git a/third_party/blink/renderer/platform/media/web_media_player_impl.cc b/third_party/blink/renderer/platform/media/web_media_player_impl.cc
index a59a5eb..3ececbd 100644
--- a/third_party/blink/renderer/platform/media/web_media_player_impl.cc
+++ b/third_party/blink/renderer/platform/media/web_media_player_impl.cc
@@ -90,17 +90,19 @@
 #include "media/base/android/media_codec_util.h"
 #endif
 
-using blink::WebMediaPlayer;
-using blink::WebString;
-
 #define STATIC_ASSERT_ENUM(a, b)                            \
   static_assert(static_cast<int>(a) == static_cast<int>(b), \
                 "mismatching enums: " #a)
 
-namespace media {
-
+namespace blink {
 namespace {
 
+namespace learning = ::media::learning;
+using ::media::Demuxer;
+using ::media::MediaLogEvent;
+using ::media::MediaLogProperty;
+using ::media::MediaTrack;
+
 const char kWatchTimeHistogram[] = "Media.WebMediaPlayerImpl.WatchTime";
 
 void RecordSimpleWatchTimeUMA(media::RendererType type) {
@@ -3900,7 +3902,7 @@
   learning::LearningTask task = learning::MediaLearningTasks::Get(task_name);
   DCHECK_EQ(task.name, task_name);
 
-  mojo::Remote<media::learning::mojom::LearningTaskController> remote_ltc;
+  mojo::Remote<learning::mojom::LearningTaskController> remote_ltc;
   media_metrics_provider_->AcquireLearningTaskController(
       task.name, remote_ltc.BindNewPipeAndPassReceiver());
   return std::make_unique<learning::MojoLearningTaskController>(
@@ -3922,4 +3924,4 @@
          base::TimeDelta::FromSeconds(5);
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/web_media_player_impl_unittest.cc b/third_party/blink/renderer/platform/media/web_media_player_impl_unittest.cc
index d14062b..ed48b089 100644
--- a/third_party/blink/renderer/platform/media/web_media_player_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/media/web_media_player_impl_unittest.cc
@@ -78,8 +78,13 @@
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "ui/gfx/geometry/size.h"
 
+namespace blink {
+namespace {
+
 using ::base::test::RunClosure;
 using ::base::test::RunOnceCallback;
+using ::media::TestAudioConfig;
+using ::media::TestVideoConfig;
 using ::testing::_;
 using ::testing::AnyNumber;
 using ::testing::DoAll;
@@ -95,8 +100,6 @@
 using ::testing::WithArg;
 using ::testing::WithoutArgs;
 
-namespace media {
-
 constexpr char kAudioOnlyTestFile[] = "sfx-opus-441.webm";
 constexpr char kVideoOnlyTestFile[] = "bear-320x240-video-only.webm";
 constexpr char kVideoAudioTestFile[] = "bear-320x240-16x9-aspect.webm";
@@ -311,6 +314,8 @@
                void(const viz::SurfaceId&, media::VideoRotation, bool));
 };
 
+}  // namespace
+
 class WebMediaPlayerImplTest
     : public testing::Test,
       private blink::WebTestingSupport::WebScopedMockScrollbars {
@@ -1796,7 +1801,7 @@
   auto mock_renderer_factory = std::make_unique<media::MockRendererFactory>();
   EXPECT_CALL(*mock_renderer_factory, CreateRenderer(_, _, _, _, _, _))
       .WillOnce(testing::WithoutArgs(Invoke([]() {
-        auto mock_renderer = std::make_unique<NiceMock<MockRenderer>>();
+        auto mock_renderer = std::make_unique<NiceMock<media::MockRenderer>>();
         EXPECT_CALL(*mock_renderer, OnSetCdm(_, _))
             .WillOnce(RunOnceCallback<1>(true));
         EXPECT_CALL(*mock_renderer, OnInitialize(_, _, _))
@@ -2472,4 +2477,4 @@
         ::testing::Bool(),
         ::testing::Bool()));
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/web_media_player_params.cc b/third_party/blink/renderer/platform/media/web_media_player_params.cc
index 84c75dd..2198aeef 100644
--- a/third_party/blink/renderer/platform/media/web_media_player_params.cc
+++ b/third_party/blink/renderer/platform/media/web_media_player_params.cc
@@ -9,7 +9,7 @@
 #include "media/base/audio_renderer_sink.h"
 #include "media/base/demuxer.h"
 
-namespace media {
+namespace blink {
 
 WebMediaPlayerParams::WebMediaPlayerParams(
     std::unique_ptr<media::MediaLog> media_log,
@@ -67,4 +67,4 @@
   return std::move(demuxer_override_);
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/web_media_source_impl.cc b/third_party/blink/renderer/platform/media/web_media_source_impl.cc
index 1cfc182..80b8f0c 100644
--- a/third_party/blink/renderer/platform/media/web_media_source_impl.cc
+++ b/third_party/blink/renderer/platform/media/web_media_source_impl.cc
@@ -12,10 +12,7 @@
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/renderer/platform/media/web_source_buffer_impl.h"
 
-using ::blink::WebString;
-using ::blink::WebMediaSource;
-
-namespace media {
+namespace blink {
 
 #define STATIC_ASSERT_MATCHING_STATUS_ENUM(webkit_name, chromium_name)    \
   static_assert(static_cast<int>(WebMediaSource::webkit_name) ==          \
@@ -107,4 +104,4 @@
   demuxer_->UnmarkEndOfStream();
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/web_media_source_impl.h b/third_party/blink/renderer/platform/media/web_media_source_impl.h
index 83b8851b..f308fdab 100644
--- a/third_party/blink/renderer/platform/media/web_media_source_impl.h
+++ b/third_party/blink/renderer/platform/media/web_media_source_impl.h
@@ -14,6 +14,9 @@
 class AudioDecoderConfig;
 class ChunkDemuxer;
 class VideoDecoderConfig;
+}  // namespace media
+
+namespace blink {
 
 class PLATFORM_EXPORT WebMediaSourceImpl : public blink::WebMediaSource {
  public:
@@ -42,6 +45,6 @@
   media::ChunkDemuxer* demuxer_;  // Owned by WebMediaPlayerImpl.
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_WEB_MEDIA_SOURCE_IMPL_H_
diff --git a/third_party/blink/renderer/platform/media/web_source_buffer_impl.cc b/third_party/blink/renderer/platform/media/web_source_buffer_impl.cc
index 71b35f7b..765d34a76 100644
--- a/third_party/blink/renderer/platform/media/web_source_buffer_impl.cc
+++ b/third_party/blink/renderer/platform/media/web_source_buffer_impl.cc
@@ -20,7 +20,7 @@
 #include "third_party/blink/public/platform/web_media_player.h"
 #include "third_party/blink/public/platform/web_source_buffer_client.h"
 
-namespace media {
+namespace blink {
 
 static blink::WebSourceBufferClient::ParseWarning ParseWarningToBlink(
     const media::SourceBufferParseWarning warning) {
@@ -259,4 +259,4 @@
   client_->NotifyParseWarning(ParseWarningToBlink(warning));
 }
 
-}  // namespace media
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/web_source_buffer_impl.h b/third_party/blink/renderer/platform/media/web_source_buffer_impl.h
index 62c9191..00f82ec 100644
--- a/third_party/blink/renderer/platform/media/web_source_buffer_impl.h
+++ b/third_party/blink/renderer/platform/media/web_source_buffer_impl.h
@@ -19,6 +19,9 @@
 class ChunkDemuxer;
 class MediaTracks;
 enum class SourceBufferParseWarning;
+}  // namespace media
+
+namespace blink {
 
 class PLATFORM_EXPORT WebSourceBufferImpl : public blink::WebSourceBuffer {
  public:
@@ -74,6 +77,6 @@
   base::TimeDelta append_window_end_;
 };
 
-}  // namespace media
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_WEB_SOURCE_BUFFER_IMPL_H_
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 9026c89..8f1dd339 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -395,6 +395,7 @@
 # Fixed in CompositeAfterPaint.
 crbug.com/918155 virtual/prefer_compositing_to_lcd_text/scrollbars/overlay-scrollbar-over-child-layer-nested-2.html [ Crash Failure Pass ]
 crbug.com/918155 virtual/prefer_compositing_to_lcd_text/scrollbars/overlay-scrollbar-over-child-layer-nested.html [ Failure ]
+crbug.com/1227541 external/wpt/css/css-multicol/multicol-scroll-content.html [ Failure ]
 
 # WebGPU tests are only run on GPU bots, so they are skipped by default and run
 # separately from other Web Tests.
@@ -4591,6 +4592,10 @@
 crbug.com/1162205 [ Win ] virtual/schemeful-same-site/external/wpt/cookies/attributes/path-redirect.html [ Crash Pass ]
 crbug.com/1162205 [ Win ] external/wpt/cookies/attributes/path-redirect.html [ Crash Pass ]
 
+# Temporarily disable failing Cookie size tests (until we implement the latest spec changes)
+crbug.com/1225342 external/wpt/cookies/size/attributes.www.sub.html [ Failure ]
+crbug.com/1225342 external/wpt/cookies/size/name-and-value.html [ Failure ]
+
 # The virtual tests run with Schemeful Same-Site disabled. These should fail to ensure the disabled feature code paths work.
 crbug.com/1127348 virtual/schemeful-same-site/external/wpt/cookies/schemeful-same-site/schemeful-websockets.sub.tentative.html [ Failure ]
 crbug.com/1127348 virtual/schemeful-same-site/external/wpt/cookies/schemeful-same-site/schemeful-iframe-subresource.tentative.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/cookies/resources/cookie-test.js b/third_party/blink/web_tests/external/wpt/cookies/resources/cookie-test.js
index 9a96506..8096ffc5 100644
--- a/third_party/blink/web_tests/external/wpt/cookies/resources/cookie-test.js
+++ b/third_party/blink/web_tests/external/wpt/cookies/resources/cookie-test.js
@@ -166,3 +166,12 @@
     CTLS: ctlCodes.map(i => ({code: i, chr: String.fromCharCode(i)}))
   };
 }
+
+// Returns a cookie string with name set to "t" * nameLength and value
+// set to "1" * valueLength. Passing in 0 for either allows for creating
+// a name- or value-less cookie.
+//
+// Note: Cookie length checking should ignore the "=".
+function cookieStringWithNameAndValueLengths(nameLength, valueLength) {
+  return `${"t".repeat(nameLength)}=${"1".repeat(valueLength)}`;
+}
diff --git a/third_party/blink/web_tests/external/wpt/cookies/size/attributes.www.sub.html b/third_party/blink/web_tests/external/wpt/cookies/size/attributes.www.sub.html
new file mode 100644
index 0000000..3004007
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/cookies/size/attributes.www.sub.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<html>
+
+<head>
+  <meta charset=utf-8>
+  <title>Test cookie attribute size restrictions</title>
+  <meta name=help href="https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4">
+  <meta name="timeout" content="long">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/cookies/resources/cookie-test.js"></script>
+</head>
+
+<body>
+  <div id=log></div>
+  <script>
+    const host = "{{host}}";
+    const attrSizeTests = [
+      {
+        cookie: `test=1; path=/cookies/size; path=/cookies/siz${"e".repeat(1024)}`,
+        expected: "test=1",
+        name: "Too long path attribute (>1024 bytes) is ignored; previous valid path wins.",
+        defaultPath: false,
+      },
+      {
+        cookie: `test=2; path=/cookies/siz${"e".repeat(1024)}; path=/cookies/size`,
+        expected: "test=2",
+        name: "Too long path attribute (>1024 bytes) is ignored; next valid path wins.",
+        defaultPath: false,
+      },
+      {
+        // This page opens on the www subdomain, so we set domain to {{host}}
+        // to see if anything works as expected.
+        cookie: `test=3; domain=${host}; domain=${"a".repeat(1024)}.com`,
+        expected: "test=3",
+        name: "Too long domain attribute (>1024 bytes) is ignored; previous valid domain wins."
+      },
+      {
+        cookie: `test=4; domain=${"a".repeat(1024)}.com; domain=${host}`,
+        expected: "test=4",
+        name: "Too long domain attribute (>1024 bytes) is ignored; next valid domain wins."
+      }
+    ];
+
+    for (const test of attrSizeTests) {
+      httpCookieTest(test.cookie, test.expected, test.name, test.defaultPath);
+    }
+  </script>
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/cookies/size/name-and-value.html b/third_party/blink/web_tests/external/wpt/cookies/size/name-and-value.html
new file mode 100644
index 0000000..2122c0d4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/cookies/size/name-and-value.html
@@ -0,0 +1,76 @@
+<!doctype html>
+<html>
+
+<head>
+  <meta charset=utf-8>
+  <title>Test cookie name size restrictions</title>
+  <meta name=help href="https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4">
+  <meta name="timeout" content="long">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/cookies/resources/cookie-test.js"></script>
+</head>
+
+<body>
+  <div id=log></div>
+  <script>
+    const nameAndValueSizeTests = [
+      {
+        cookie: cookieStringWithNameAndValueLengths(2048, 2048),
+        expected: cookieStringWithNameAndValueLengths(2048, 2048),
+        name: "Set max-size cookie with largest possible name and value (4096 bytes)",
+      },
+      {
+        cookie: cookieStringWithNameAndValueLengths(4097, 1),
+        expected: "",
+        name: "Ignore cookie with name larger than 4096 and 1 byte value",
+      },
+      {
+        cookie: cookieStringWithNameAndValueLengths(4096, 0),
+        expected: cookieStringWithNameAndValueLengths(4096, 0),
+        name: "Set max-size value-less cookie",
+      },
+      {
+        cookie: cookieStringWithNameAndValueLengths(4097, 0),
+        expected: "",
+        name: "Ignore value-less cookie with name larger than 4096 bytes",
+      },
+      {
+        cookie: cookieStringWithNameAndValueLengths(1, 4095),
+        expected: cookieStringWithNameAndValueLengths(1, 4095),
+        name: "Set max-size cookie with largest possible value (4095 bytes)",
+      },
+      {
+        cookie: cookieStringWithNameAndValueLengths(1, 4096),
+        expected: "",
+        name: "Ignore named cookie (with non-zero length) and value larger than 4095 bytes",
+      },
+      {
+        cookie: cookieStringWithNameAndValueLengths(4096, 1),
+        expected: "",
+        name: "Ignore named cookie with length larger than 4095 bytes, and a non-zero value",
+      },
+      {
+        cookie: cookieStringWithNameAndValueLengths(0, 4096),
+        expected: cookieStringWithNameAndValueLengths(0, 4096).slice(1), // it won't come back with leading =
+        name: "Set max-size name-less cookie",
+      },
+      {
+        cookie: cookieStringWithNameAndValueLengths(0, 4097),
+        expected: "",
+        name: "Ignore name-less cookie with value larger than 4096 bytes",
+      },
+      {
+        cookie: cookieStringWithNameAndValueLengths(0, 4097).slice(1), // slice off leading =
+        expected: "",
+        name: "Ignore name-less cookie (without leading =) with value larger than 4096 bytes",
+      },
+    ];
+
+    for (const test of nameAndValueSizeTests) {
+      httpCookieTest(test.cookie, test.expected, test.name);
+    }
+  </script>
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-scroll-content-ref.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-scroll-content-ref.html
new file mode 100644
index 0000000..320e425
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-scroll-content-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<div style="overflow: scroll; width: 100px; height: 150px; background: green">
+  <div style="width: 400px; height: 400px"></div>
+</div>
+<div style="overflow: scroll; width: 100px; height: 150px;
+            position: relative; top: -50px; left: 100px; background: green">
+  <div style="width: 400px; height: 400px"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-scroll-content.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-scroll-content.html
new file mode 100644
index 0000000..9fdaf1a8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-scroll-content.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<title>CSS Test: Multi-column element with scrolled content</title>
+<link rel="help" href="http://www.w3.org/TR/css3-multicol/#columns"/>
+<link rel="match" href="multicol-scroll-content-ref.html"/>
+<div style="columns: 2; width: 200px; height: 100px; column-gap: 0">
+  <div style="overflow: scroll; height: 150px; background: red">
+    <div style="width: 400px; height: 400px; background: green; position: relative"></div>
+  </div>
+</div>
+<div style="columns: 2; width: 200px; height: 100px; column-gap: 0">
+  <div style="height: 100px"></div>
+  <div style="overflow: scroll; height: 150px; background: red">
+    <div style="width: 400px; height: 400px; background: green; position: relative"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/fast/canvas/layers-loneendlayer-expected.html b/third_party/blink/web_tests/fast/canvas/layers-loneendlayer-expected.html
new file mode 100644
index 0000000..3c3b6e0
--- /dev/null
+++ b/third_party/blink/web_tests/fast/canvas/layers-loneendlayer-expected.html
@@ -0,0 +1,19 @@
+<body>
+<script>
+var canvas, ctx;
+canvas = document.createElement("canvas");
+canvas.width = 200;
+canvas.height = 200;
+document.body.appendChild(canvas);
+
+ctx = canvas.getContext("2d");
+ctx.fillStyle = 'rgba(0,0,255,1)';
+ctx.fillRect(60,60,75,50);
+ctx.globalAlpha = 0.5;
+
+ctx.fillStyle = 'rgba(225,0,0,1)';
+ctx.fillRect(50,50,75,50);
+ctx.fillStyle = 'rgba(0,255,0,1)';
+ctx.fillRect(70,70,75,50);
+</script>
+</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/canvas/layers-loneendlayer.html b/third_party/blink/web_tests/fast/canvas/layers-loneendlayer.html
new file mode 100644
index 0000000..5e9646a1
--- /dev/null
+++ b/third_party/blink/web_tests/fast/canvas/layers-loneendlayer.html
@@ -0,0 +1,24 @@
+<body>
+<script>
+/*
+  A test to make sure a single endLayer with no saveLayer is a no-op.
+*/
+var canvas, ctx;
+canvas = document.createElement("canvas");
+canvas.width = 200;
+canvas.height = 200;
+document.body.appendChild(canvas);
+
+ctx = canvas.getContext("2d");
+ctx.fillStyle = 'rgba(0,0,255,1)';
+ctx.fillRect(60,60,75,50);
+ctx.globalAlpha = 0.5;
+
+ctx.fillStyle = 'rgba(225,0,0,1)';
+ctx.fillRect(50,50,75,50);
+ctx.fillStyle = 'rgba(0,255,0,1)';
+ctx.fillRect(70,70,75,50);
+
+ctx.endLayer();
+</script>
+</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 54960be..60eee1d 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -1014,6 +1014,7 @@
     getter textWordSpacing
     method arc
     method arcTo
+    method beginLayer
     method beginPath
     method bezierCurveTo
     method clearRect
@@ -1028,6 +1029,7 @@
     method createRadialGradient
     method drawImage
     method ellipse
+    method endLayer
     method fill
     method fillRect
     method fillText
diff --git a/third_party/blink/web_tests/http/tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/http/tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 2279ae5..30b689b 100644
--- a/third_party/blink/web_tests/http/tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -1055,6 +1055,7 @@
 [Worker]     getter textWordSpacing
 [Worker]     method arc
 [Worker]     method arcTo
+[Worker]     method beginLayer
 [Worker]     method beginPath
 [Worker]     method bezierCurveTo
 [Worker]     method clearRect
@@ -1069,6 +1070,7 @@
 [Worker]     method createRadialGradient
 [Worker]     method drawImage
 [Worker]     method ellipse
+[Worker]     method endLayer
 [Worker]     method fill
 [Worker]     method fillRect
 [Worker]     method fillText
diff --git a/third_party/blink/web_tests/http/tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/http/tests/webexposed/global-interface-listing-expected.txt
index 5ed4e23..ec001807 100644
--- a/third_party/blink/web_tests/http/tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/http/tests/webexposed/global-interface-listing-expected.txt
@@ -1102,6 +1102,7 @@
     method addHitRegion
     method arc
     method arcTo
+    method beginLayer
     method beginPath
     method bezierCurveTo
     method clearHitRegions
@@ -1117,6 +1118,7 @@
     method drawFocusIfNeeded
     method drawImage
     method ellipse
+    method endLayer
     method fill
     method fillFormattedText
     method fillRect
@@ -5962,6 +5964,7 @@
     getter textWordSpacing
     method arc
     method arcTo
+    method beginLayer
     method beginPath
     method bezierCurveTo
     method clearRect
@@ -5976,6 +5979,7 @@
     method createRadialGradient
     method drawImage
     method ellipse
+    method endLayer
     method fill
     method fillRect
     method fillText
diff --git a/third_party/blink/web_tests/virtual/anonymous-iframe/http/tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/anonymous-iframe/http/tests/webexposed/global-interface-listing-expected.txt
index 022721a..6569f65 100644
--- a/third_party/blink/web_tests/virtual/anonymous-iframe/http/tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/anonymous-iframe/http/tests/webexposed/global-interface-listing-expected.txt
@@ -1102,6 +1102,7 @@
     method addHitRegion
     method arc
     method arcTo
+    method beginLayer
     method beginPath
     method bezierCurveTo
     method clearHitRegions
@@ -1117,6 +1118,7 @@
     method drawFocusIfNeeded
     method drawImage
     method ellipse
+    method endLayer
     method fill
     method fillFormattedText
     method fillRect
@@ -5964,6 +5966,7 @@
     getter textWordSpacing
     method arc
     method arcTo
+    method beginLayer
     method beginPath
     method bezierCurveTo
     method clearRect
@@ -5978,6 +5981,7 @@
     method createRadialGradient
     method drawImage
     method ellipse
+    method endLayer
     method fill
     method fillRect
     method fillText
diff --git a/tools/ipc_fuzzer/fuzzer/BUILD.gn b/tools/ipc_fuzzer/fuzzer/BUILD.gn
index 4a38553..b835438 100644
--- a/tools/ipc_fuzzer/fuzzer/BUILD.gn
+++ b/tools/ipc_fuzzer/fuzzer/BUILD.gn
@@ -20,7 +20,7 @@
     "rand_util.h",
   ]
   deps = [
-    "//base/util/type_safety",
+    "//base",
     "//components/viz/common",
     "//printing/mojom:mojom_shared_cpp_sources",
     "//services/device/public/mojom:mojom_headers",
diff --git a/tools/ipc_fuzzer/fuzzer/fuzzer.cc b/tools/ipc_fuzzer/fuzzer/fuzzer.cc
index 7b0242a..1bda469 100644
--- a/tools/ipc_fuzzer/fuzzer/fuzzer.cc
+++ b/tools/ipc_fuzzer/fuzzer/fuzzer.cc
@@ -14,8 +14,8 @@
 #include "base/cxx17_backports.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_util.h"
+#include "base/types/id_type.h"
 #include "base/unguessable_token.h"
-#include "base/util/type_safety/id_type.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
@@ -1029,8 +1029,8 @@
 };
 
 template <typename TypeMarker, typename WrappedType, WrappedType kInvalidValue>
-struct FuzzTraits<util::IdType<TypeMarker, WrappedType, kInvalidValue>> {
-  using param_type = util::IdType<TypeMarker, WrappedType, kInvalidValue>;
+struct FuzzTraits<base::IdType<TypeMarker, WrappedType, kInvalidValue>> {
+  using param_type = base::IdType<TypeMarker, WrappedType, kInvalidValue>;
   static bool Fuzz(param_type* id, Fuzzer* fuzzer) {
     WrappedType raw_value = id->GetUnsafeValue();
     if (!FuzzParam(&raw_value, fuzzer))
diff --git a/tools/mac/power/OWNERS b/tools/mac/power/OWNERS
new file mode 100644
index 0000000..cc0895a
--- /dev/null
+++ b/tools/mac/power/OWNERS
@@ -0,0 +1,2 @@
+olivierli@chromium.org
+etiennep@chromium.org
diff --git a/tools/mac/power/README.md b/tools/mac/power/README.md
new file mode 100644
index 0000000..9549824
--- /dev/null
+++ b/tools/mac/power/README.md
@@ -0,0 +1,41 @@
+# Usage scenario scripts
+
+This directory contains the necessary files to make Chromium based browsers and Safari
+execute usage scenarios that represent real world usage.
+
+The scripts differ slightly from similar tools like telemetry tests in that they work for Safari.
+
+## Scenarios
+Scenarios are a set of operations to be applied on a set of URLs.
+
+For example:
+* Navigate to google.com
+* Open the first link
+* Wait 60 seconds
+
+It's interesting to gather power metrics, profiles and traces for specific
+scenarios to understand their performance characteristics.
+
+# Usage
+
+First `generate_scripts.py` needs to be used to convert the templates in `driver_script_templates/` into
+working AppleScript. The templating allows for the generation of scripts that work with many different
+browsers in the same way without having to modify each file by hand which is error
+prone.
+
+Once generated the driver scripts are found in `driver_scripts/` and can be invoked directly like this:
+```
+osascript ./driver_scripts/chrome_navigation.scpt
+```
+
+Once the scenario has run it's course the script will exit. If the desired the
+browser can be opened by hand before running the scenario to modify the starting
+state.
+
+# Formats
+
+Files in `driver_script_templates/` directory that do not end in .scpt are
+jinja2 templates that need to be rendered into usable Applescript.
+
+Files in `driver_script_templates/` that end in .scpt are already working
+Applescript and will be copied as is to `driver_script/`.
diff --git a/tools/mac/power/driver_scripts_templates/aligned_timers b/tools/mac/power/driver_scripts_templates/aligned_timers
new file mode 100644
index 0000000..4f62e87
--- /dev/null
+++ b/tools/mac/power/driver_scripts_templates/aligned_timers
@@ -0,0 +1,38 @@
+{%- from 'macros' import input with context -%}
+{{ hash_bang }}
+
+-- Copyright 2021 The Chromium Authors. All rights reserved.
+-- Use of this source code is governed by a BSD-style license that can be
+-- found in the LICENSE file.
+
+-- This scripts opens a few staggered windows to spam_tasks.html so that
+-- they are all visible and javascript tasks all execute unthrottled.
+
+tell application "{{ browser }}"
+    -- If {{ browser }} is already started then just bring
+    -- it to the forefront otherwise open it.
+    if it is running then
+      reopen
+    else
+      activate
+    end if
+
+    set w to first window
+
+    set bounds of front window to {0, 0, 1920, 1080}
+
+    set base_url to "file:///{{ directory }}/pages/spam_tasks.html?interval=200"
+
+    set num_peers to 4
+    repeat with i from 1 to num_peers
+      make new window
+      set bounds of front window to {1080 / num_peers*i, 0, 1920, 1080}
+      set full_url to base_url & "&numPeers=" & num_peers & "&peerID=" & i
+      open location full_url
+    end repeat
+
+    delay 60
+
+    activate
+
+end tell
diff --git a/tools/mac/power/driver_scripts_templates/idle.scpt b/tools/mac/power/driver_scripts_templates/idle.scpt
new file mode 100755
index 0000000..2db3e90
--- /dev/null
+++ b/tools/mac/power/driver_scripts_templates/idle.scpt
@@ -0,0 +1,9 @@
+#!/usr/bin/osascript
+
+-- Copyright 2021 The Chromium Authors. All rights reserved.
+-- Use of this source code is governed by a BSD-style license that can be
+-- found in the LICENSE file.
+
+-- This script simply waits for |delay| seconds before quiting.
+
+delay {{ delay }}
diff --git a/tools/mac/power/driver_scripts_templates/idle_on_site b/tools/mac/power/driver_scripts_templates/idle_on_site
new file mode 100644
index 0000000..8e1134b
--- /dev/null
+++ b/tools/mac/power/driver_scripts_templates/idle_on_site
@@ -0,0 +1,29 @@
+{%- from 'macros' import input with context -%}
+{{ hash_bang }}
+
+-- Copyright 2021 The Chromium Authors. All rights reserved.
+-- Use of this source code is governed by a BSD-style license that can be
+-- found in the LICENSE file.
+
+-- This script navigates to |idle_site| and remains on it for |delay| seconds.
+
+tell application "{{ browser }}"
+    -- If {{ browser }} is already started then just bring
+    -- it to the forefront otherwise open it.
+    if it is running then
+      reopen
+    else
+      activate
+    end if
+
+    set bounds of front window to {0, 0, 1920, 1080}
+
+    delay 10
+
+    open location "{{ idle_site }}"
+
+    delay {{ delay }}
+
+    activate
+
+end tell
diff --git a/tools/mac/power/driver_scripts_templates/meet b/tools/mac/power/driver_scripts_templates/meet
new file mode 100644
index 0000000..bf4fc5c
--- /dev/null
+++ b/tools/mac/power/driver_scripts_templates/meet
@@ -0,0 +1,33 @@
+{%- from 'macros' import input with context -%}
+{{ hash_bang }}
+
+-- Copyright 2021 The Chromium Authors. All rights reserved.
+-- Use of this source code is governed by a BSD-style license that can be
+-- found in the LICENSE file.
+
+-- This script navigates to a Meet meeting (id: |meeting_id|) and clicks the
+-- join button.
+
+set myURL to "https://meet.google.com/{{ meeting_id }}"
+
+tell application "{{ browser }}"
+    -- If {{ browser }} is already started then just bring
+    -- it to the forefront otherwise open it.
+    if it is running then
+      reopen
+    else
+      activate
+    end if
+
+    set bounds of front window to {0, 0, 1920, 1080}
+
+    open location {myURL}
+    tell tab 1 of window 1
+        repeat while (loading)
+            delay 1
+        end repeat
+
+        execute javascript "b=document.getElementsByClassName('uArJ5e UQuaGc Y5sE8d uyXBBb xKiqt')[0];b.click();"
+        delay 1800
+    end tell
+end tell
diff --git a/tools/mac/power/driver_scripts_templates/navigation b/tools/mac/power/driver_scripts_templates/navigation
new file mode 100644
index 0000000..e8b1de75
--- /dev/null
+++ b/tools/mac/power/driver_scripts_templates/navigation
@@ -0,0 +1,52 @@
+{%- from 'macros' import input with context -%}
+{{ hash_bang }}
+
+-- Copyright 2021 The Chromium Authors. All rights reserved.
+-- Use of this source code is governed by a BSD-style license that can be
+-- found in the LICENSE file.
+
+-- This script cycles through a set of N sites staying on each site for
+-- |per_navigation_delay| seconds before moving on to the next site. The full
+-- set of sites of visited |navigation_cycles| times before the script exits.
+-- Navigation happens by creating a new tab and then closing it.
+
+tell application "{{ browser }}"
+
+    -- If {{ browser }} is already started then just bring
+    -- it to the forefront otherwise open it.
+    if it is running then
+      reopen
+    else
+      activate
+    end if
+
+    if it is running then
+      activate
+      set w to first window
+      set bounds of front window to {0, 0, 1920, 1080}
+
+      -- Ensure a tab stays live all the time so the window doesn't go away.
+      open location "about:blank"
+
+      -- Each cycles takes about 2 mins. Aim for a test that takes an hour.
+      repeat with i from 1 to {{ navigation_cycles }}
+
+        set sites to {"https://amazon.com", "https://www.youtube.com"} & ¬
+          {"https://www.google.com", "https://github.com/chromium/chromium"}
+
+        repeat with site in sites
+          open location site
+
+          delay {{ per_navigation_delay }}
+
+          tell active tab of w
+            close
+          end tell
+        end repeat
+
+      end repeat
+
+      activate
+    end if
+
+end tell
diff --git a/tools/mac/power/driver_scripts_templates/open_background b/tools/mac/power/driver_scripts_templates/open_background
new file mode 100644
index 0000000..1d7cb3f
--- /dev/null
+++ b/tools/mac/power/driver_scripts_templates/open_background
@@ -0,0 +1,41 @@
+{%- from 'macros' import input with context -%}
+{{ hash_bang }}
+
+-- Copyright 2021 The Chromium Authors. All rights reserved.
+-- Use of this source code is governed by a BSD-style license that can be
+-- found in the LICENSE file.
+
+-- This site opens all sites identified in |background_sites| and then waits
+-- |delay| on the last tab opened.
+
+tell application "{{ browser }}"
+
+  if it is running then
+    reopen
+  else
+    activate
+  end if
+
+  if it is running then
+    activate
+    set w to first window
+    set bounds of front window to {0, 0, 1920, 1080}
+
+    set sites to { {{ background_sites }} }
+    repeat with site in sites
+      open location site
+
+      -- Wait for the page to be fully loaded.
+      tell active tab of w
+        repeat while (loading)
+            delay 1
+        end repeat
+      end tell
+
+    end repeat
+
+    delay {{ delay }}
+
+  end if
+
+end tell
diff --git a/tools/mac/power/driver_scripts_templates/prep_safari.scpt b/tools/mac/power/driver_scripts_templates/prep_safari.scpt
new file mode 100755
index 0000000..bd66f95
--- /dev/null
+++ b/tools/mac/power/driver_scripts_templates/prep_safari.scpt
@@ -0,0 +1,16 @@
+#!/usr/bin/osascript
+
+-- Copyright 2021 The Chromium Authors. All rights reserved.
+-- Use of this source code is governed by a BSD-style license that can be
+-- found in the LICENSE file.
+
+-- This script makes sure that Safari is always in the same state to start
+-- scenarios. This is because tab recovery is automatic and recovered tabs
+-- need to be closed to get a clean slate.
+
+tell application "Safari"
+    activate
+    reopen
+    close (every tab of window 1)
+    close every window
+end tell
diff --git a/tools/mac/power/driver_scripts_templates/safari_idle_on_site b/tools/mac/power/driver_scripts_templates/safari_idle_on_site
new file mode 100644
index 0000000..51ec312
--- /dev/null
+++ b/tools/mac/power/driver_scripts_templates/safari_idle_on_site
@@ -0,0 +1,30 @@
+{%- from 'macros' import input with context -%}
+{{ hash_bang }}
+
+-- Copyright 2021 The Chromium Authors. All rights reserved.
+-- Use of this source code is governed by a BSD-style license that can be
+-- found in the LICENSE file.
+
+-- For behavior description see idle_on_site file. The only difference with the
+-- generic template is that the way to access windows is different.
+
+tell application "Safari"
+  -- If Safari is already started then just bring
+  -- it to the forefront otherwise open it.
+  if it is running then
+    reopen
+  else
+    activate
+  end if
+
+  set w to first window
+  set bounds of w to {0, 0, 1920, 1080}
+
+  delay 10
+
+  open location "{{ idle_site }}"
+
+  delay {{ delay }}
+
+  activate
+end tell
diff --git a/tools/mac/power/driver_scripts_templates/safari_meet b/tools/mac/power/driver_scripts_templates/safari_meet
new file mode 100644
index 0000000..13b08d0b
--- /dev/null
+++ b/tools/mac/power/driver_scripts_templates/safari_meet
@@ -0,0 +1,36 @@
+{%- from 'macros' import input with context -%}
+{{ hash_bang }}
+
+-- Copyright 2021 The Chromium Authors. All rights reserved.
+-- Use of this source code is governed by a BSD-style license that can be
+-- found in the LICENSE file.
+
+-- For behavior description see meet file. Differs from the generic template
+-- because for Safari Javascript is invoked differently and tabs are accessed
+-- differently.
+
+set myURL to "https://meet.google.com/{{ meeting_id }}"
+
+tell application "Safari"
+  -- If Safari is already started then just bring
+  -- it to the forefront otherwise open it.
+  if it is running then
+    reopen
+  else
+    activate
+  end if
+
+  set w to first window
+  set bounds of w to {0, 0, 1920, 1080}
+
+  open location {myURL}
+  tell document 1
+    repeat while document 1's source = ""
+      delay 0.5
+    end repeat
+
+    do JavaScript "b=document.getElementsByClassName('uArJ5e UQuaGc Y5sE8d uyXBBb xKiqt')[0];b.click();"
+  end tell
+
+  delay 1800
+end tell
diff --git a/tools/mac/power/driver_scripts_templates/safari_navigation b/tools/mac/power/driver_scripts_templates/safari_navigation
new file mode 100644
index 0000000..c90e79c
--- /dev/null
+++ b/tools/mac/power/driver_scripts_templates/safari_navigation
@@ -0,0 +1,43 @@
+{%- from 'macros' import input with context -%}
+{{ hash_bang }}
+
+-- Copyright 2021 The Chromium Authors. All rights reserved.
+-- Use of this source code is governed by a BSD-style license that can be
+-- found in the LICENSE file.
+
+-- For behavior description see navigation file. Differs from the generic
+-- template becauses tabs and windows are not acessesed in the same way and
+-- making sure only one tab is shown at all times is handled differently.
+
+tell application "Safari"
+
+    -- If Safari is already started then just bring
+    -- it to the forefront otherwise open it.
+    if it is running then
+      reopen
+    else
+      activate
+    end if
+
+    set w to first window
+    set bounds of w to {0, 0, 1920, 1080}
+
+    -- Each cycles takes about 2 mins. Aim for a test that takes an hour.
+    repeat with i from 1 to {{ navigation_cycles }}
+
+      set sites to {"https://amazon.com", "https://www.youtube.com"} & ¬
+        {"https://www.google.com", "https://github.com/chromium/chromium"}
+
+      repeat with site in sites
+        open location site
+
+        delay {{ per_navigation_delay }}
+
+        set t to current tab of w
+        close t
+      end repeat
+    end repeat
+
+    activate
+
+end tell
diff --git a/tools/mac/power/driver_scripts_templates/safari_open_background b/tools/mac/power/driver_scripts_templates/safari_open_background
new file mode 100644
index 0000000..c763274
--- /dev/null
+++ b/tools/mac/power/driver_scripts_templates/safari_open_background
@@ -0,0 +1,38 @@
+{%- from 'macros' import input with context -%}
+{{ hash_bang }}
+
+-- Copyright 2021 The Chromium Authors. All rights reserved.
+-- Use of this source code is governed by a BSD-style license that can be
+-- found in the LICENSE file.
+
+-- For behavior description see open_background file. Differs from the generic
+-- template becauses tabs and windows are not acessesed in the same way for
+-- Safari. The way to wait for the page to be done loading is also different.
+
+tell application "Safari"
+  -- If Safari is already started then just bring
+  -- it to the forefront otherwise open it.
+  if it is running then
+    reopen
+  else
+    activate
+  end if
+
+  set w to first window
+  set bounds of w to {0, 0, 1920, 1080}
+
+  set sites to { {{ background_sites }} }
+  repeat with site in sites
+    open location site
+
+    -- Wait for the page to be fully loaded.
+    repeat while document 1's source = ""
+      delay 0.5
+    end repeat
+
+  end repeat
+
+  delay {{ delay }}
+
+  activate
+end tell
diff --git a/tools/mac/power/driver_scripts_templates/safari_scroll b/tools/mac/power/driver_scripts_templates/safari_scroll
new file mode 100644
index 0000000..2845dd8
--- /dev/null
+++ b/tools/mac/power/driver_scripts_templates/safari_scroll
@@ -0,0 +1,46 @@
+{%- from 'macros' import input with context -%}
+{{ hash_bang }}
+
+-- Copyright 2021 The Chromium Authors. All rights reserved.
+-- Use of this source code is governed by a BSD-style license that can be
+-- found in the LICENSE file.
+
+-- For behavior description see scroll file. Differs from the generic
+-- template becauses tabs and windows are not acessesed in the same way for
+-- Safari. The way to wait for the page to be done loading is also different.
+-- The way to invoke javascript is also different.
+
+
+tell application "Safari"
+    -- If Safari is already started then just bring
+    -- it to the forefront otherwise open it.
+    if it is running then
+      reopen
+    else
+      activate
+    end if
+
+    set w to first window
+    set bounds of w to {0, 0, 1920, 1080}
+
+    open location "http://reddit.com"
+
+    repeat while document 1's source = ""
+      delay 0.5
+    end repeat
+
+    tell document 1
+      repeat with i from 1 to {{ navigation_cycles }}
+
+        -- set the vertical scroll
+        repeat with y from 90 to 100
+          do javascript "h=document.documentElement.scrollHeight-document.documentElement.clientHeight; window.scrollTo(0,h*" & 100 & "/100)"
+          delay 0.25
+        end repeat
+
+        delay 3
+
+      end repeat
+    end tell
+
+end tell
diff --git a/tools/mac/power/driver_scripts_templates/safari_zero_window b/tools/mac/power/driver_scripts_templates/safari_zero_window
new file mode 100644
index 0000000..cc17c3db
--- /dev/null
+++ b/tools/mac/power/driver_scripts_templates/safari_zero_window
@@ -0,0 +1,31 @@
+{%- from 'macros' import input with context -%}
+{{ hash_bang }}
+
+-- Copyright 2021 The Chromium Authors. All rights reserved.
+-- Use of this source code is governed by a BSD-style license that can be
+-- found in the LICENSE file.
+
+-- For behavior description see zero_window file. The only difference with the
+-- generic template is that the way to access windows is different.
+
+tell application "Safari"
+  -- If Safari is already started then just bring
+  -- it to the forefront otherwise open it.
+  if it is running then
+    reopen
+  else
+    activate
+  end if
+
+  set w to first window
+  set bounds of w to {0, 0, 1920, 1080}
+
+  open location "about:blank"
+  delay 10
+
+  close every window
+
+  delay {{ delay }}
+
+  activate
+end tell
diff --git a/tools/mac/power/driver_scripts_templates/scroll b/tools/mac/power/driver_scripts_templates/scroll
new file mode 100644
index 0000000..e46d40b
--- /dev/null
+++ b/tools/mac/power/driver_scripts_templates/scroll
@@ -0,0 +1,45 @@
+{%- from 'macros' import input with context -%}
+{{ hash_bang }}
+
+-- Copyright 2021 The Chromium Authors. All rights reserved.
+-- Use of this source code is governed by a BSD-style license that can be
+-- found in the LICENSE file.
+
+-- This script navigates to the homepage of reddit.com and then scrolls down the
+-- page continously. The script scrolls |navigation_cycles| times. Scrolling is
+-- done through javascript with window.scrollTo. It's done in increments to
+-- react to the fact the the DOM changes with content getting loaded.
+
+set myURL to "https://reddit.com"
+set scrollAmount to "100" --- % down the page
+
+tell application "{{ browser }}"
+
+    -- If {{ browser }} is already started then just bring
+    -- it to the forefront otherwise open it.
+    if it is running then
+      reopen
+    else
+      activate
+    end if
+
+    set bounds of front window to {0, 0, 1920, 1080}
+
+    tell front window to set curTab to make new tab at after (get active tab) with properties {URL:myURL}
+    tell curTab
+        repeat while (loading)
+            delay 1
+        end repeat
+
+        repeat with i from 1 to {{ navigation_cycles }}
+
+          -- set the vertical scroll
+          repeat with y from 90 to 100
+            execute javascript "h=document.documentElement.scrollHeight-document.documentElement.clientHeight; window.scrollTo(0,h*" & y & "/100)"
+            delay 0.25
+          end repeat
+
+          delay 3
+        end repeat
+    end tell
+end tell
diff --git a/tools/mac/power/driver_scripts_templates/zero_window b/tools/mac/power/driver_scripts_templates/zero_window
new file mode 100644
index 0000000..da253ed
--- /dev/null
+++ b/tools/mac/power/driver_scripts_templates/zero_window
@@ -0,0 +1,32 @@
+{%- from 'macros' import input with context -%}
+{{ hash_bang }}
+
+-- Copyright 2021 The Chromium Authors. All rights reserved.
+-- Use of this source code is governed by a BSD-style license that can be
+-- found in the LICENSE file.
+
+-- This script will navigate to about:blank and then close the browser window.
+-- This lets the browser open and only present in the doc with now visible
+-- window.
+
+tell application "{{ browser }}"
+    -- If {{ browser }} is already started then just bring
+    -- it to the forefront otherwise open it.
+    if it is running then
+      reopen
+    else
+      activate
+    end if
+
+    set bounds of front window to {0, 0, 1920, 1080}
+
+    open location "about:blank"
+    delay 10
+
+    close every window
+
+    delay {{ delay }}
+
+    activate
+
+end tell
diff --git a/tools/mac/power/generate_scripts.py b/tools/mac/power/generate_scripts.py
new file mode 100755
index 0000000..ec4fa978
--- /dev/null
+++ b/tools/mac/power/generate_scripts.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python3
+
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from jinja2 import Template
+from jinja2 import FileSystemLoader
+from jinja2.environment import Environment
+from utils import BROWSERS_DEFINITION
+
+import argparse
+import os
+import shutil
+""" Script to generate browser driver scripts from templates.
+
+The generated scripts can be used to have browsers go
+through scenarios in a repeatable way.
+"""
+
+
+def get_render_targets(template_file, output_filename):
+  """For certain scenarios more than one driver script is generated.
+  Return a list dicts that describes them all."""
+
+  # In the case of idle_on_site render for different sites.
+  if template_file.endswith("idle_on_site"):
+    render_targets = []
+    render_targets.append({
+        "output_filename":
+        output_filename.replace("site", "wiki"),
+        "idle_site":
+        "http://www.wikipedia.com/wiki/Alessandro_Volta"
+    })
+    render_targets.append({
+        "output_filename":
+        output_filename.replace("site", "youtube"),
+        "idle_site":
+        "https://www.youtube.com/watch?v=9EE_ICC_wFw?autoplay=1"
+    })
+    return render_targets
+
+  return [{"output_filename": output_filename}]
+
+
+def render(file_prefix, template_file, process_name, extra_args):
+  """Render a single scenario script."""
+
+  if file_prefix:
+    file_prefix = file_prefix.replace(" ", "_") + "_"
+    file_prefix = file_prefix.lower()
+
+  # Roughly the Alexa top 50 at the time of writing this.
+  background_sites_list = [
+      "https://google.com", "https://youtube.com", "https://tmall.com",
+      "https://baidu.com", "https://qq.com", "https://sohu.com",
+      "https://amazon.com", "https://taobao.com", "https://facebook.com",
+      "https://360.cn", "https://yahoo.com", "https://jd.com",
+      "https://wikipedia.org", "https://zoom.us", "https://sina.com.cn",
+      "https://weibo.com", "https://live.com", "https://xinhuanet.com",
+      "https://reddit.com", "https://microsoft.com", "https://netflix.com",
+      "https://office.com", "https://microsoftonline.com",
+      "https://okezone.com", "https://vk.com", "https://myshopify.com",
+      "https://panda.tv", "https://alipay.com", "https://csdn.net",
+      "https://instagram.com", "https://zhanqi.tv", "https://yahoo.co.jp",
+      "https://ebay.com", "https://apple.com", "https://bing.com",
+      "https://bongacams.com", "https://google.com.hk", "https://naver.com",
+      "https://stackoverflow.com", "https://aliexpress.com",
+      "https://twitch.tv", "https://amazon.co.jp", "https://amazon.in",
+      "https://adobe.com", "https://tianya.cn", "https://huanqiu.com",
+      "https://aparat.com", "https://amazonaws.com", "https://twitter.com",
+      "https://yy.com"
+  ]
+  background_sites = ",".join(background_sites_list)
+
+  output_filename = f"./driver_scripts/{file_prefix}{template_file}.scpt"
+
+  for render_target in get_render_targets(template_file, output_filename):
+
+    render_target = {**render_target, **extra_args}
+
+    env = Environment()
+    env.loader = FileSystemLoader('.')
+    template = env.get_template("driver_scripts_templates/" + template_file)
+
+    with open(render_target["output_filename"], 'w') as output:
+      output.write(
+          template.render(**render_target,
+                          directory=os.getcwd(),
+                          background_sites=background_sites,
+                          navigation_cycles=30,
+                          per_navigation_delay=30,
+                          delay=3600,
+                          browser=process_name))
+
+
+def render_runner_scripts(extra_args):
+  """Render all scenario driver scripts for all browsers (if applicable)."""
+
+  # Generate all driver scripts from templates.
+  for _, _, files in os.walk("./driver_scripts_templates"):
+    for template_file in files:
+      if not template_file.endswith(".scpt") and not template_file.endswith(
+          ".swp"):
+        print(template_file)
+        if template_file.startswith("safari"):
+          # Generate for Safari
+          render("", template_file, "", extra_args)
+        else:
+          # Generate for all Chromium based browsers
+          for browser in ['Chrome', 'Canary', "Chromium", "Edge"]:
+            process_name = BROWSERS_DEFINITION[browser]["process_name"]
+            render(browser, template_file, process_name, extra_args)
+
+
+def generate_all(extra_args):
+  """Delete all existing generated scripts. Scripts should not be
+  modified by hand.
+  """
+
+  shutil.rmtree("driver_scripts/", ignore_errors=True)
+  os.makedirs("driver_scripts", exist_ok=True)
+
+  # Generate scripts for all scenarios.
+  render_runner_scripts(extra_args)
+
+  # Copy the files that don't need any substitutions.
+  for _, _, files in os.walk("./driver_scripts_templates"):
+    for script in files:
+      if script.endswith(".scpt"):
+        shutil.copyfile(f"./driver_scripts_templates/{script}",
+                        f"./driver_scripts/{script}")
+
+
+if __name__ == "__main__":
+  parser = argparse.ArgumentParser(
+      description='Generate browser driver scripts to execute usage scenarios.')
+  parser.add_argument("--meet_meeting_id",
+                      help="ID of meeting for Meet base scnearios.",
+                      required=False)
+  args = parser.parse_args()
+
+  extra_args = {"hash_bang": "#!/usr/bin/osascript"}
+  if args.meet_meeting_id:
+    extra_args["meeting_id"] = args.meet_meeting_id
+
+  generate_all(extra_args)
diff --git a/tools/mac/power/macros b/tools/mac/power/macros
new file mode 100644
index 0000000..c6b7b25
--- /dev/null
+++ b/tools/mac/power/macros
@@ -0,0 +1,9 @@
+{# Copyright 2021 The Chromium Authors. All rights reserved. #}
+{# Use of this source code is governed by a BSD-style license that can be #}
+{# found in the LICENSE file. #}
+
+{# This file contains common blocks that can be reused in driver_script_templates #}
+
+{% macro hash_bang() -%}
+  #!/usr/bin/osascript
+{%- endmacro %}
diff --git a/tools/mac/power/pages/spam_tasks.html b/tools/mac/power/pages/spam_tasks.html
new file mode 100644
index 0000000..f3539b2
--- /dev/null
+++ b/tools/mac/power/pages/spam_tasks.html
@@ -0,0 +1,12 @@
+<!--This is a very unteresting page that does nothing but load js. Look to the associated .js file for comments.-->
+<!--The page has to be invoked with the correct arguments like so: -->
+<!--file:///Users/$USER/git/chrome_safari_power/pages/spam_tasks.html?interval=200&peerID=1&numPeers=3-->
+<script src="spam_tasks.js"></script>
+<html>
+  <head>
+    <title>Spam Tasks!</title>
+  </head>
+  <body onload="onLoad()" onunload="cleanup()">
+  </body>
+</html>
+
diff --git a/tools/mac/power/pages/spam_tasks.js b/tools/mac/power/pages/spam_tasks.js
new file mode 100644
index 0000000..9ac8afc
--- /dev/null
+++ b/tools/mac/power/pages/spam_tasks.js
@@ -0,0 +1,94 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This code is used to execute costly repeating timers on different renderers.
+// The renderers communicate using broacast channels to make sure the timers are
+// unaligned. See the HTML file to know how to invoke this with the correct
+// parameters.
+
+// Try to make this a more pleasant experience by failing fast.
+'use strict';
+
+// Setup the broadcast channel to talk to the other pages.
+const bc = new BroadcastChannel('broadcast_channel');
+bc.onmessage = onConnect;
+
+// Store the params passed in directly to the page.
+var param_dict = {};
+
+// Simply return a random string of the appropriate length.
+function getRandomString(length) {
+  var result           = '';
+  var characters       =
+      'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+  var charactersLength = characters.length;
+  for ( var i = 0; i < length; i++ ) {
+    result += characters.charAt(Math.floor(Math.random() * charactersLength));
+  }
+  return result;
+}
+
+// Completely change the content of the page html to trigger rendering.
+function changeInnerHTMLBody() {
+  document.body.innerHTML = getRandomString(1000);
+}
+
+// Kick-off the repeating timer and warn the next peer to start also.
+function startRepeatingTasks(interval){
+
+  // Find out how long this specific peer has to wait.
+  var peer_id = parseInt(param_dict["peerID"]);
+  var wait_length = parseInt(param_dict["interval"]) /
+      parseInt(param_dict["numPeers"]) * peer_id;
+
+  // Busy loop until we are aligned.
+  var now = Date.now();
+  while(now % interval != 0){
+    now = Date.now();
+  }
+
+  // Sleep for the additional time. This makes sure the tasks are unaligned.
+  var deadline = now + wait_length;
+  while(now < deadline){
+    now = Date.now();
+  }
+
+  // Start then warn peers.
+  setInterval(changeInnerHTMLBody, interval);
+  bc.postMessage(JSON.stringify(param_dict));
+}
+
+// When peer n-1 has started we can start ourselves.
+function onConnect(event){
+  var result = JSON.parse(event.data);
+
+  // Only react to peer n-1 starting.
+  if(parseInt(result["peerID"]) == parseInt(param_dict["peerID"]) - 1){
+
+    startRepeatingTasks(parseInt(param_dict["interval"]));
+    console.log(result);
+  }
+}
+
+function onLoad() {
+  // Get the params from the URL
+  var params = window.location.search.slice(1).split("&");
+  params.forEach(function(pair){
+    var split = pair.split("=");
+    var name = split[0];
+    var value = split[1];
+    param_dict[name] = value;
+  });
+
+  var interval = parseInt(param_dict["interval"]);
+
+  // First page can't wait for anybody else to start!
+  if(param_dict["peerID"] == "1"){
+    startRepeatingTasks(interval);
+  }
+}
+
+function cleaup() {
+  bc.close();
+}
diff --git a/tools/mac/power/utils.py b/tools/mac/power/utils.py
new file mode 100644
index 0000000..0033fa3
--- /dev/null
+++ b/tools/mac/power/utils.py
@@ -0,0 +1,80 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+SCENARIOS = [{
+    "name": "idle"
+}, {
+    "name": "canary_idle_on_youtube_slack",
+    "browser": "Canary"
+}, {
+    "name": "canary_idle_on_youtube_noslack",
+    "browser": "Canary"
+}, {
+    "name": "safari_idle_on_youtube",
+    "browser": "Safari"
+}, {
+    "name": "canary_idle_on_wiki_slack",
+    "browser": "Canary"
+}, {
+    "name": "canary_idle_on_wiki_noslack",
+    "browser": "Canary"
+}, {
+    "name": "chrome_navigation",
+    "browser": "Chrome"
+}, {
+    "name": "safari_navigation",
+    "browser": "Safari"
+}, {
+    "name": "chrome_idle_on_wiki",
+    "browser": "Chrome"
+}, {
+    "name": "safari_idle_on_wiki",
+    "browser": "Safari"
+}, {
+    "name": "chrome_idle_on_wiki_hidden",
+    "browser": "Chrome"
+}, {
+    "name": "safari_idle_on_wiki_hidden",
+    "browser": "Safari"
+}, {
+    "name": "chrome_idle_on_youtube",
+    "browser": "Chrome"
+}, {
+    "name": "safari_idle_on_youtube",
+    "browser": "Safari"
+}, {
+    "name": "chrome_zero_window",
+    "browser": "Chrome"
+}, {
+    "name": "safari_zero_window",
+    "browser": "Safari"
+}]
+
+BROWSERS_DEFINITION = {
+    "Chrome": {
+        "executable": "Google Chrome",
+        "process_name": "Google Chrome",
+        "identifier": "com.google.Chrome"
+    },
+    "Canary": {
+        "executable": "Google Chrome Canary",
+        "process_name": "Google Chrome Canary",
+        "identifier": "com.google.Chrome.canary"
+    },
+    "Chromium": {
+        "process_name": "Chromium",
+        "executable": "Chromium",
+        "identifier": "org.chromium.Chromium"
+    },
+    "Edge": {
+        "executable": "Microsoft Edge",
+        "process_name": "Microsoft Edge",
+        "identifier": "com.microsoft.edgemac"
+    },
+    "Safari": {
+        "executable": "Safari",
+        "process_name": "Safari",
+        "identifier": "com.apple.Safari"
+    }
+}
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 8b607e6..679b41d5 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -7506,6 +7506,7 @@
   <int value="15" label="EMF_INVALID_EXTENSION_ID_FOR_EXTENSION_SOURCE"/>
   <int value="16" label="EMF_INVALID_EXTENSION_ID_FOR_CONTENT_SCRIPT"/>
   <int value="17" label="EMF_INVALID_EXTENSION_ID_FOR_WORKER_CONTEXT"/>
+  <int value="18" label="EMF_INVALID_PORT_CONTEXT"/>
 </enum>
 
 <enum name="BadMessageReasonGuestView">
@@ -18083,6 +18084,18 @@
   <int value="5" label="D3D_FEATURE_LEVEL_11_1"/>
 </enum>
 
+<enum name="D3DShaderModel">
+  <int value="0" label="UNKNOWN_OR_NO_SUPPORTED_DEVICES"/>
+  <int value="1" label="D3D_SHADER_MODEL_5_1"/>
+  <int value="2" label="D3D_SHADER_MODEL_6_0"/>
+  <int value="3" label="D3D_SHADER_MODEL_6_1"/>
+  <int value="4" label="D3D_SHADER_MODEL_6_2"/>
+  <int value="5" label="D3D_SHADER_MODEL_6_3"/>
+  <int value="6" label="D3D_SHADER_MODEL_6_4"/>
+  <int value="7" label="D3D_SHADER_MODEL_6_5"/>
+  <int value="8" label="D3D_SHADER_MODEL_6_6"/>
+</enum>
+
 <enum name="DailyEventIntervalType">
   <int value="0" label="First Run"/>
   <int value="1" label="Day Elapsed"/>
@@ -18130,6 +18143,13 @@
   <int value="2" label="Dark theme"/>
 </enum>
 
+<enum name="DataChannelAggregateType">
+  <int value="0" label="Unreliable Unordered."/>
+  <int value="1" label="Unreliable Ordered"/>
+  <int value="2" label="Reliable Unordered."/>
+  <int value="3" label="Reliable Ordered"/>
+</enum>
+
 <enum name="DataChannelCounters">
   <int value="0" label="Channel created."/>
   <int value="1" label="Channel reached Open state."/>
@@ -18138,6 +18158,24 @@
   <int value="4" label="Channel is negotiated."/>
 </enum>
 
+<enum name="DataChannelSctpErrorCode">
+  <int value="0" label="Unspecified"/>
+  <int value="1" label="InvalidStreamIdentifier"/>
+  <int value="2" label="MissingMandatoryParameter"/>
+  <int value="3" label="StaleCookieError"/>
+  <int value="4" label="OutOfResource"/>
+  <int value="5" label="UnresolvableAddress"/>
+  <int value="6" label="UnspkUnrecognizedChunkTypeecified"/>
+  <int value="7" label="InvalidMandatoryParameter"/>
+  <int value="8" label="UnrecognizedParameters"/>
+  <int value="9" label="NoUserData"/>
+  <int value="10" label="CookieReceivedWhileShuttingDown"/>
+  <int value="11" label="RestartWithNewAddresses"/>
+  <int value="12" label="UserInitiatedAbort"/>
+  <int value="13" label="ProtocolViolation"/>
+  <int value="14" label="Other"/>
+</enum>
+
 <enum name="DataPackLoadErrors">
   <int value="1" label="INIT_FAILED (obsolete)"/>
   <int value="2" label="BAD_VERSION"/>
@@ -20018,6 +20056,7 @@
   <int value="108" label="Extension install friction"/>
   <int value="109" label="File handling permission request"/>
   <int value="110" label="Enterprise confirmation"/>
+  <int value="111" label="App Identity Update Confirmation"/>
 </enum>
 
 <enum name="DialogOriginRelationship">
@@ -33901,6 +33940,11 @@
   <int value="3950" label="SanitizerAPIElementSetSanitized"/>
   <int value="3951" label="TextShadowInHighlightPseudo"/>
   <int value="3952" label="TextShadowNotNoneInHighlightPseudo"/>
+  <int value="3953" label="SameSiteNoneRequired"/>
+  <int value="3954" label="SameSiteNoneIncludedBySamePartyTopResource"/>
+  <int value="3955" label="SameSiteNoneIncludedBySamePartyAncestors"/>
+  <int value="3956" label="SameSiteNoneIncludedBySameSiteLax"/>
+  <int value="3957" label="SameSiteNoneIncludedBySameSiteStrict"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -47178,6 +47222,8 @@
   <int value="-1251359563" label="SharingRenameDevices:disabled"/>
   <int value="-1250965985" label="MediaFeeds:enabled"/>
   <int value="-1250611337" label="ChromeVoxArcSupport:disabled"/>
+  <int value="-1250326683"
+      label="MagnifierCaretFollowingFromJavascript:enabled"/>
   <int value="-1248478422" label="enable-zip-archiver-packer"/>
   <int value="-1246840031" label="OptInImeMenu:disabled"/>
   <int value="-1245617305" label="NewProfilePicker:enabled"/>
@@ -47632,6 +47678,7 @@
   <int value="-880087793" label="OmniboxPedalsBatch2:enabled"/>
   <int value="-879055117" label="ClipboardContentSetting:enabled"/>
   <int value="-879031960" label="FetchKeepaliveTimeoutSetting:disabled"/>
+  <int value="-879024703" label="pwa-update-dialog-for-name-and-icon:enabled"/>
   <int value="-876773120" label="TabbedAppOverflowMenuActionBar:enabled"/>
   <int value="-876148583" label="ArcBootCompletedBroadcast:disabled"/>
   <int value="-875217114"
@@ -48595,6 +48642,7 @@
   <int value="-45487487" label="NewTabPageCustomLinks:disabled"/>
   <int value="-45074716" label="SystemDownloadManager:disabled"/>
   <int value="-45067971" label="NewPrintPreview:disabled"/>
+  <int value="-44325492" label="PdfUnseasoned:enabled"/>
   <int value="-43980455" label="MobileIdentityConsistencyFRE:enabled"/>
   <int value="-43650386" label="WindowsFollowCursor:enabled"/>
   <int value="-43428597" label="ClickToCallDetectionV2:enabled"/>
@@ -49158,6 +49206,7 @@
   <int value="423855924" label="enable-tab-switcher-theme-colors"/>
   <int value="425072496" label="GridLayoutForNtpShortcuts:enabled"/>
   <int value="426199960" label="Commander:enabled"/>
+  <int value="426540253" label="PowerScheduler:disabled"/>
   <int value="427184788" label="WebFeed:enabled"/>
   <int value="427945554" label="StrictExtensionIsolation:enabled"/>
   <int value="430776375" label="TextureLayerSkipWaitForActivation:disabled"/>
@@ -49277,6 +49326,8 @@
   <int value="519140642" label="SendWebUIJavaScriptErrorReports:enabled"/>
   <int value="520738365" label="OmniboxPedalsBatch2NonEnglish:enabled"/>
   <int value="520982116" label="BuiltInModuleAll:enabled"/>
+  <int value="523885852"
+      label="MagnifierCaretFollowingFromJavascript:disabled"/>
   <int value="529235584" label="PhoneHub:enabled"/>
   <int value="530828403" label="AllowStartingServiceManagerOnly:disabled"/>
   <int value="533064367" label="WebRtcHideLocalIpsWithMdns:disabled"/>
@@ -49472,6 +49523,7 @@
   <int value="680300401" label="DisplayAlignAssist:disabled"/>
   <int value="681622885" label="FullscreenExitUI:enabled"/>
   <int value="682549212" label="ash-enable-cursor-motion-blur"/>
+  <int value="682916740" label="PdfUnseasoned:disabled"/>
   <int value="683013217" label="AllowPopupsDuringPageUnload:disabled"/>
   <int value="683433500" label="CommerceMerchantViewer:disabled"/>
   <int value="684806628" label="TranslateLanguageByULP:disabled"/>
@@ -50531,6 +50583,7 @@
   <int value="1569982806" label="BentoBar:enabled"/>
   <int value="1570178909" label="NewOverviewLayout:enabled"/>
   <int value="1571998166" label="DetectingHeavyPages:disabled"/>
+  <int value="1572464760" label="pwa-update-dialog-for-name-and-icon:disabled"/>
   <int value="1573860827" label="EnableImeSandbox:enabled"/>
   <int value="1575978252" label="PasswordLeakDetection:enabled"/>
   <int value="1577205328"
@@ -50882,6 +50935,7 @@
   <int value="1858385315" label="ChromeHomeBottomNavLabels:enabled"/>
   <int value="1858919054" label="SplitSettingsSync:disabled"/>
   <int value="1860597983" label="AndroidSpellChecker:disabled"/>
+  <int value="1860955818" label="PowerScheduler:enabled"/>
   <int value="1861251313"
       label="enable-message-center-always-scroll-up-upon-notification-removal"/>
   <int value="1861521561" label="OmniboxLocalEntitySuggestions:enabled"/>
@@ -60586,9 +60640,11 @@
     The download was a valid CRX file but came from a publisher that is
     untrusted.
   </int>
-  <int value="11" label="Model directory does not exist">
-    The directory used to store models does not exist so the model could not be
-    stored for use.
+  <int value="11" label="OptGuide parent directory does not exist">
+    The parent directory used to store models does not exist.
+  </int>
+  <int value="12" label="Model directory could not be created">
+    The directory needed to store this model version could not be created.
   </int>
 </enum>
 
@@ -71259,6 +71315,20 @@
   <int value="11" label="Lax to Cross context with Lax cookie; secure origin."/>
 </enum>
 
+<enum name="SameSiteNonePartyContextType">
+  <summary>
+    Indicates the strictest cookie semantics that could have included the given
+    SameSite=None cookie on this access.
+  </summary>
+  <int value="0" label="Requires SameSite=None"/>
+  <int value="1"
+      label="Included under SameParty if using top frame and resource URL
+             only"/>
+  <int value="2" label="Included under SameParty if using all ancestor frames"/>
+  <int value="3" label="Included under SameSite=Lax"/>
+  <int value="4" label="Included under SameSite=Strict"/>
+</enum>
+
 <enum name="SamlChallengeKeyHandlerResult">
   <summary>
     Tracks results of calculating response for a challenge from Verified Access
@@ -81269,6 +81339,14 @@
   <int value="80" label="TRIM_MEMORY_COMPLETE"/>
 </enum>
 
+<enum name="TrustedVaultDeviceRegistrationState">
+  <int value="0" label="Already registered"/>
+  <int value="1" label="Local keys are stale"/>
+  <int value="2" label="Throttled client side"/>
+  <int value="3" label="Attempting registration with newly generated keys"/>
+  <int value="4" label="Attempting registration with preexisting keys"/>
+</enum>
+
 <enum name="TrustedVaultFetchKeysAttempt">
   <int value="0" label="First attempt"/>
   <int value="1" label="Second attempt"/>
@@ -85957,6 +86035,8 @@
   <int value="17" label="Forget site dialog opened"/>
   <int value="18" label="Forget site data cleared"/>
   <int value="19" label="History page opened"/>
+  <int value="20" label="History page individual entry removed"/>
+  <int value="21" label="History page individual entry clicked"/>
 </enum>
 
 <enum name="WebSiteSettingsAllSitesAction">
diff --git a/tools/metrics/histograms/histograms_xml/android/histograms.xml b/tools/metrics/histograms/histograms_xml/android/histograms.xml
index a637074..51d144e 100644
--- a/tools/metrics/histograms/histograms_xml/android/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/android/histograms.xml
@@ -1666,7 +1666,7 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Android.Messages.Dismissed{MessageIdentifier}"
+<histogram name="Android.Messages.Dismissed{MessageIdentifier}"
     enum="MessageDismissReason" expires_after="2022-06-01">
   <owner>pavely@chromium.org</owner>
   <owner>lazzzis@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/apps/histograms.xml b/tools/metrics/histograms/histograms_xml/apps/histograms.xml
index 5fcc164..2a6ea252 100644
--- a/tools/metrics/histograms/histograms_xml/apps/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/apps/histograms.xml
@@ -32,6 +32,8 @@
   <variant name="PluginVm" summary="Plugin VM app"/>
   <variant name="RemoteApp" summary="Remote apps"/>
   <variant name="StandaloneBrowser" summary="Standalone browsers"/>
+  <variant name="StandaloneBrowserExtension"
+      summary="Extension based apps hosted by lacros."/>
   <variant name="SystemWebApp" summary="System web apps"/>
   <variant name="WebApp" summary="Web apps"/>
 </variants>
diff --git a/tools/metrics/histograms/histograms_xml/cookie/histograms.xml b/tools/metrics/histograms/histograms_xml/cookie/histograms.xml
index 5dfcf35..ffee23b 100644
--- a/tools/metrics/histograms/histograms_xml/cookie/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/cookie/histograms.xml
@@ -685,6 +685,22 @@
   </summary>
 </histogram>
 
+<histogram name="Cookie.SameSiteNone.PartyContext.{AccessType}"
+    enum="SameSiteNonePartyContextType" expires_after="2022-06-15">
+  <owner>cfredric@chromium.org</owner>
+  <owner>kaustubhag@chromium.org</owner>
+  <summary>
+    The strictest semantics that could have included a particular SameSite=None
+    cookie for the {AccessType} in the current context.
+
+    Recorded once per included SameSite=None cookie per {AccessType}.
+  </summary>
+  <token key="AccessType">
+    <variant name="Read" summary="read"/>
+    <variant name="Write" summary="write"/>
+  </token>
+</histogram>
+
 <histogram name="Cookie.SameSiteNoneIsSecure" enum="Boolean"
     expires_after="2021-10-25">
   <owner>chlily@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/download/histograms.xml b/tools/metrics/histograms/histograms_xml/download/histograms.xml
index bf47291..379c384 100644
--- a/tools/metrics/histograms/histograms_xml/download/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/download/histograms.xml
@@ -1104,7 +1104,7 @@
     A download warning was shown in the shelf. Note that some downloads may not
     be shown on the shelf, e.g., if chrome://downloads is already open when the
     download completes, or if an extension is using the downloads API. Grouped
-    by the type of danger.
+    by the type of danger. This histogram is logged on ChromeOS since M93.
 
     This metric is used to populate a dashboard on go/crsb-site.
   </summary>
diff --git a/tools/metrics/histograms/histograms_xml/gpu/histograms.xml b/tools/metrics/histograms/histograms_xml/gpu/histograms.xml
index 20bfe13..82ec6ee 100644
--- a/tools/metrics/histograms/histograms_xml/gpu/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/gpu/histograms.xml
@@ -357,6 +357,16 @@
   </summary>
 </histogram>
 
+<histogram name="GPU.D3D12HighestShaderModel" enum="D3DShaderModel"
+    expires_after="2022-01-07">
+  <owner>magchen@chromium.org</owner>
+  <owner>zmo@chromium.org</owner>
+  <summary>
+    The maximum supported D3D shader model version on a D3D12 device. It is
+    recorded in the browser process 120 seconds after the browser launch.
+  </summary>
+</histogram>
+
 <histogram name="GPU.D3DShaderModel" enum="ShaderModel"
     expires_after="2021-08-29">
   <owner>jonahr@google.com</owner>
diff --git a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
index cf4dc62d..a391fa2 100644
--- a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
@@ -7833,6 +7833,8 @@
       label="Showing cache patterns only for AnswerSuggestions."/>
   <suffix name="AssistantDetails"
       label="Showing cache patterns only for AssistantDetails."/>
+  <suffix name="AutofillImageFetcher"
+      label="Show customized card art image for credit card."/>
   <suffix name="ContextualSuggestions"
       label="Showing cache patterns only for ContextualSuggestions."/>
   <suffix name="Cryptids" label="Asset fetcher for cryptid rendering."/>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index 4abe846..f0cfde1 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -10640,6 +10640,28 @@
   </summary>
 </histogram>
 
+<histogram name="NoteCreation.TimeTo{action}" units="ms"
+    expires_after="2021-12-01">
+  <owner>sebsg@chromium.org</owner>
+  <owner>chrome-creation@google.com</owner>
+  <summary>
+    Records the amount of time taken to accomplish {action} from the time the
+    creation flow was started.
+  </summary>
+  <token key="action">
+    <variant name=".DismissCreationDialog"
+        summary="The user dismissed the creation dialog."/>
+    <variant name=".DismissShare"
+        summary="The user dismissed the sharing dialog for their newly
+                 created note."/>
+    <variant name=".SelectTemplate"
+        summary="The user selected a template for their note, completing the
+                 creation flow."/>
+    <variant name=".ShareCreation"
+        summary="The user shared their newly created note."/>
+  </token>
+</histogram>
+
 <histogram name="NQE.CachedNetworkQualityAvailable" enum="BooleanAvailable"
     expires_after="2021-10-10">
   <owner>tbansal@chromium.org</owner>
@@ -14007,6 +14029,9 @@
 
 <histogram name="SB2.RemoteCall.ThreatSubType.PotentiallyHarmfulApp"
     enum="SB2RemoteCallThreatSubType" expires_after="2021-07-28">
+  <obsolete>
+    Removed from code Jul 2021.
+  </obsolete>
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -14018,6 +14043,9 @@
 
 <histogram name="SB2.RemoteCall.ThreatSubType.SocialEngineering"
     enum="SB2RemoteCallThreatSubType" expires_after="2021-07-28">
+  <obsolete>
+    Removed from code Jul 2021.
+  </obsolete>
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/quota/histograms.xml b/tools/metrics/histograms/histograms_xml/quota/histograms.xml
index e44c004..4d96884 100644
--- a/tools/metrics/histograms/histograms_xml/quota/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/quota/histograms.xml
@@ -52,7 +52,7 @@
 </histogram>
 
 <histogram name="Quota.AvailableDiskSpace2" units="MB"
-    expires_after="2021-08-15">
+    expires_after="2022-07-07">
   <owner>jarrydg@chromium.org</owner>
   <owner>chrome-owp-storage@google.com</owner>
   <summary>
@@ -240,7 +240,7 @@
 </histogram>
 
 <histogram name="Quota.SkippedInvalidOriginUsage" enum="InvalidOriginReason"
-    expires_after="2021-08-04">
+    expires_after="2022-07-07">
   <owner>jarrydg@chromium.org</owner>
   <owner>chrome-owp-storage@google.com</owner>
   <summary>
@@ -284,7 +284,7 @@
   </summary>
 </histogram>
 
-<histogram name="Quota.UsageByOrigin" units="MB" expires_after="2021-07-03">
+<histogram name="Quota.UsageByOrigin" units="MB" expires_after="2022-07-07">
   <owner>jarrydg@chromium.org</owner>
   <owner>chrome-owp-storage@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/sync/histograms.xml b/tools/metrics/histograms/histograms_xml/sync/histograms.xml
index a6971b3..446c873 100644
--- a/tools/metrics/histograms/histograms_xml/sync/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/sync/histograms.xml
@@ -1371,6 +1371,18 @@
   </summary>
 </histogram>
 
+<histogram name="Sync.TrustedVaultDeviceRegistrationState"
+    enum="TrustedVaultDeviceRegistrationState" expires_after="2021-11-01">
+  <owner>mmoskvitin@google.com</owner>
+  <owner>mastiz@chromium.org</owner>
+  <component>Services&gt;Sync</component>
+  <summary>
+    Records whether the local device is registered on the server upon startup
+    (if signed in) or upon first signin, and if not registered, provides
+    insights into why.
+  </summary>
+</histogram>
+
 <histogram name="Sync.TrustedVaultErrorShownOnStartup" enum="Boolean"
     expires_after="2021-11-01">
   <owner>mmoskvitin@google.com</owner>
diff --git a/tools/metrics/histograms/histograms_xml/variations/histograms.xml b/tools/metrics/histograms/histograms_xml/variations/histograms.xml
index 039a2c5..c81ebf2 100644
--- a/tools/metrics/histograms/histograms_xml/variations/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/variations/histograms.xml
@@ -377,17 +377,27 @@
     What behavior of Safe Mode for CachedFlags (including not engaging it) was
     performed.
 
-    &quot;Engaged&quot; is recorded at the point where whether to engage safe
-    mode is decided. &quot;Cached&quot; is recorded at the point where flags
-    were cached from native to Shared Preferences. The ratio &quot;Cached&quot;
-    / &quot;Engaged&quot; is the success rate for a given mode, and (100% - this
-    ratio) is the crash rate.
+    {Event}
+
+    The ratio &quot;Cached&quot; / (&quot;WillCache&quot; - &quot;Pause&quot;)
+    is the success rate for a given mode, and (100% - success rate) is the crash
+    rate.
 
     Android only.
   </summary>
   <token key="Event">
-    <variant name="Cached"/>
-    <variant name="Engaged"/>
+    <variant name="Cached"
+        summary="&quot;Cached&quot; is recorded at the point where flags were
+                 cached from native to Shared Preferences."/>
+    <variant name="Engaged"
+        summary="&quot;Engaged&quot; is recorded at the point where whether
+                 to engage safe mode is decided."/>
+    <variant name="Pause"
+        summary="&quot;Pause&quot; is recorded when the initialization flow
+                 is aborted."/>
+    <variant name="WillCache"
+        summary="&quot;WillCache&quot; is recorded when the initialization
+                 flow that should result in caching flags is entered."/>
   </token>
 </histogram>
 
diff --git a/tools/metrics/histograms/histograms_xml/web_rtc/histograms.xml b/tools/metrics/histograms/histograms_xml/web_rtc/histograms.xml
index d06d8ac..bea5fbdb 100644
--- a/tools/metrics/histograms/histograms_xml/web_rtc/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/web_rtc/histograms.xml
@@ -176,6 +176,18 @@
   </summary>
 </histogram>
 
+<histogram name="WebRTC.Audio.Agc.InputClippingRate" units="%"
+    expires_after="2021-12-31">
+  <owner>silen@chromium.org</owner>
+  <owner>alessiob@chromium.org</owner>
+  <owner>minyue@chromium.org</owner>
+  <summary>
+    Logs the input clipping rate in AgcManagerDirect. A log call is made every
+    30 seconds during an active WebRTC call using the analog gain controller and
+    clipping detection.
+  </summary>
+</histogram>
+
 <histogram name="WebRTC.Audio.Agc2.DigitalGainApplied" units="dB"
     expires_after="2021-12-31">
   <owner>alessiob@chromium.org</owner>
@@ -1133,31 +1145,57 @@
   </summary>
 </histogram>
 
-<histogram name="WebRTC.DataChannelCounters" enum="DataChannelCounters"
-    expires_after="2020-04-19">
-  <owner>perkj@chromium.org</owner>
+<histogram name="WebRTC.DataChannelAggregateType"
+    enum="DataChannelAggregateType" expires_after="2021-12-31">
+  <owner>orphis@chromium.org</owner>
+  <owner>toprice@chromium.org</owner>
   <summary>
-    Counters on creation, opening, and a few main attributes of data channels.
+    Recorded when a data channel is created. Counts the combination of reliable
+    and ordered modes in a data channel.
+  </summary>
+</histogram>
+
+<histogram name="WebRTC.DataChannelCounters" enum="DataChannelCounters"
+    expires_after="2021-12-31">
+  <owner>orphis@chromium.org</owner>
+  <owner>toprice@chromium.org</owner>
+  <summary>
+    Recorded when a data channel is created. Counts a few main attributes of
+    data channels. Expired in April 2020. Revived in M93.
   </summary>
 </histogram>
 
 <histogram name="WebRTC.DataChannelMaxRetransmits" units="units"
-    expires_after="M85">
-  <owner>perkj@chromium.org</owner>
+    expires_after="2021-12-31">
+  <owner>orphis@chromium.org</owner>
+  <owner>toprice@chromium.org</owner>
   <summary>
-    The maximum number of retransmissions that are attempted in unreliable mode.
-    It is set to the value used in the configuration when a RTCDataChannel is
-    created.
+    Recorded when a data channel is created. The maximum number of
+    retransmissions that are attempted in unreliable mode. It is set to the
+    value used in the configuration when a RTCDataChannel is created. Expired in
+    M86. Revived in M93.
   </summary>
 </histogram>
 
 <histogram name="WebRTC.DataChannelMaxRetransmitTime" units="ms"
-    expires_after="M77">
-  <owner>perkj@chromium.org</owner>
+    expires_after="2021-12-31">
+  <owner>orphis@chromium.org</owner>
+  <owner>toprice@chromium.org</owner>
   <summary>
-    The length of the time window during which transmissions and retransmissions
-    may occur in unreliable mode. It is set to the value used in the
-    configuration when a RTCDataChannel is created.
+    Recorded when a data channel is created. The length of the time window
+    during which transmissions and retransmissions may occur in unreliable mode.
+    It is set to the value used in the configuration when a RTCDataChannel is
+    created. Expired in M78. Revived in M93.
+  </summary>
+</histogram>
+
+<histogram name="WebRTC.DataChannelSctpErrorCode"
+    enum="DataChannelSctpErrorCode" expires_after="2021-12-31">
+  <owner>orphis@chromium.org</owner>
+  <owner>toprice@chromium.org</owner>
+  <summary>
+    Recorded when a data channel is closed unexpectedly and the error event is
+    dispatched. Contains the SCTP error code signaled by the transport.
   </summary>
 </histogram>
 
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index c644b62..11be390 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -1,16 +1,16 @@
 {
     "trace_processor_shell": {
         "win": {
-            "hash": "8b4b619cf0b64565b5392c457a68b1b0fe278165",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/583e3b55bc9badcd5817f889265adeaa67a7f547/trace_processor_shell.exe"
+            "hash": "4eb9ac9b9dddb1309368e446c2e21d1cb1dd5cdd",
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/5e6406aa2903feef7e4a25d8ba79d40e212a101e/trace_processor_shell.exe"
         },
         "mac": {
             "hash": "e9c26d8ab8a0a60c936bfbd07de9074b2470b392",
             "remote_path": "perfetto_binaries/trace_processor_shell/mac/ca06eb435506f54cf6a7c013a6c545726532f2b3/trace_processor_shell"
         },
         "linux": {
-            "hash": "9a988c4bed7a5b5bd001104bcf7f38a185b715ba",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/583e3b55bc9badcd5817f889265adeaa67a7f547/trace_processor_shell"
+            "hash": "48cdc66c153d782040e5d700f46ec65465f591e6",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/71d582b360331087630937eaec1d194f0c0b78fb/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 493322b6..47ec90c 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -18,6 +18,7 @@
  <item id="appcache_update_job" added_in_milestone="62" hash_code="25790702" type="0" content_hash_code="27424887" os_list="linux,windows" file_path="content/browser/appcache/appcache_update_url_loader_request.cc"/>
  <item id="auction_downloader" added_in_milestone="92" hash_code="27833508" type="0" content_hash_code="118747666" os_list="linux,windows" file_path="content/services/auction_worklet/auction_downloader.cc"/>
  <item id="auction_report_sender" added_in_milestone="92" hash_code="96136865" type="0" content_hash_code="62368950" os_list="linux,windows" file_path="content/browser/interest_group/ad_auction_service_impl.cc"/>
+ <item id="autofill_image_fetcher_card_art_image" added_in_milestone="93" hash_code="90561372" type="0" content_hash_code="24622412" os_list="linux,windows" file_path="components/autofill/core/browser/ui/autofill_image_fetcher.cc"/>
  <item id="autofill_query" added_in_milestone="62" hash_code="88863520" type="0" content_hash_code="15563339" os_list="linux,windows" file_path="components/autofill/core/browser/autofill_download_manager.cc"/>
  <item id="autofill_upload" added_in_milestone="62" hash_code="104798869" type="0" content_hash_code="110634763" os_list="linux,windows" file_path="components/autofill/core/browser/autofill_download_manager.cc"/>
  <item id="backdrop_collection_images_download" added_in_milestone="68" hash_code="34767164" type="0" content_hash_code="62971406" os_list="linux,windows" file_path="chrome/browser/search/background/ntp_background_service.cc"/>
@@ -108,7 +109,7 @@
  <item id="downloads_api_run_async" added_in_milestone="62" hash_code="121068967" type="0" content_hash_code="9280914" os_list="linux,windows" file_path="chrome/browser/extensions/api/downloads/downloads_api.cc"/>
  <item id="downloads_dom_handler" added_in_milestone="73" hash_code="95951029" type="0" content_hash_code="137150731" os_list="linux,windows" file_path="chrome/browser/ui/webui/downloads/downloads_dom_handler.cc"/>
  <item id="drag_download_file" added_in_milestone="62" hash_code="95910019" type="0" content_hash_code="126492858" os_list="linux,windows" file_path="content/browser/download/drag_download_file.cc"/>
- <item id="drive_service" added_in_milestone="90" hash_code="56074781" type="0" content_hash_code="94248995" os_list="linux,windows" file_path="chrome/browser/search/drive/drive_service.cc"/>
+ <item id="drive_service" added_in_milestone="90" hash_code="56074781" type="0" content_hash_code="94248995" os_list="linux,windows" file_path="chrome/browser/new_tab_page/modules/drive/drive_service.cc"/>
  <item id="early_hints_preload" added_in_milestone="91" hash_code="35266994" type="0" content_hash_code="42984058" os_list="linux,windows" file_path="content/browser/loader/navigation_early_hints_manager.cc"/>
  <item id="enterprise_safe_browsing_realtime_url_lookup" added_in_milestone="86" hash_code="22262963" type="0" content_hash_code="14052810" os_list="linux,windows" file_path="chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service.cc"/>
  <item id="expect_ct_reporter" added_in_milestone="69" hash_code="23537266" type="0" content_hash_code="130492494" os_list="linux,windows" file_path="services/network/expect_ct_reporter.cc"/>
@@ -218,7 +219,7 @@
  <item id="omnibox_suggest" added_in_milestone="62" hash_code="47815025" type="0" content_hash_code="86297726" os_list="linux,windows" file_path="components/omnibox/browser/search_provider.cc"/>
  <item id="omnibox_suggest_deletion" added_in_milestone="62" hash_code="84212388" type="0" content_hash_code="24981550" os_list="linux,windows" file_path="components/omnibox/browser/base_search_provider.cc"/>
  <item id="omnibox_zerosuggest" added_in_milestone="62" hash_code="7687691" type="0" content_hash_code="119419625" os_list="linux,windows" file_path="components/omnibox/browser/remote_suggestions_service.cc"/>
- <item id="one_google_bar_service" added_in_milestone="62" hash_code="78917933" type="0" content_hash_code="46527252" os_list="linux,windows" file_path="chrome/browser/search/one_google_bar/one_google_bar_loader_impl.cc"/>
+ <item id="one_google_bar_service" added_in_milestone="62" hash_code="78917933" type="0" content_hash_code="46527252" os_list="linux,windows" file_path="chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.cc"/>
  <item id="open_screen_message" added_in_milestone="78" hash_code="95250780" type="0" deprecated="2020-02-13" content_hash_code="39027953" file_path=""/>
  <item id="open_screen_tls_message" added_in_milestone="80" hash_code="61695471" type="0" deprecated="2020-02-13" content_hash_code="28355565" file_path=""/>
  <item id="open_search" added_in_milestone="62" hash_code="107267424" type="0" content_hash_code="83025542" os_list="linux,windows" file_path="components/search_engines/template_url_fetcher.cc"/>
@@ -345,7 +346,7 @@
  <item id="sync_file_system" added_in_milestone="62" hash_code="102819690" type="0" content_hash_code="52153962" os_list="linux,windows" file_path="chrome/browser/sync_file_system/drive_backend/sync_engine.cc"/>
  <item id="sync_http_bridge" added_in_milestone="62" hash_code="57144960" type="0" content_hash_code="32868346" os_list="linux,windows" file_path="components/sync/engine/net/http_bridge.cc"/>
  <item id="sync_stop_reporter" added_in_milestone="62" hash_code="5021348" type="0" content_hash_code="56902850" os_list="linux,windows" file_path="components/sync/driver/sync_stopped_reporter.cc"/>
- <item id="task_module_service" added_in_milestone="88" hash_code="50808258" type="0" content_hash_code="12116330" os_list="linux,windows" file_path="chrome/browser/search/task_module/task_module_service.cc"/>
+ <item id="task_module_service" added_in_milestone="88" hash_code="50808258" type="0" content_hash_code="12116330" os_list="linux,windows" file_path="chrome/browser/new_tab_page/modules/task_module/task_module_service.cc"/>
  <item id="test" added_in_milestone="62" hash_code="3556498" type="0" reserved="1" os_list="linux,windows" file_path=""/>
  <item id="test_partial" added_in_milestone="62" hash_code="22096011" type="0" reserved="1" os_list="linux,windows" file_path=""/>
  <item id="tethering_handler_socket" added_in_milestone="65" hash_code="113065062" type="0" content_hash_code="986296" os_list="linux,windows" file_path="content/browser/devtools/protocol/tethering_handler.cc"/>
diff --git a/tools/traffic_annotation/summary/grouping.xml b/tools/traffic_annotation/summary/grouping.xml
index ec5a95c..bab5b35b 100644
--- a/tools/traffic_annotation/summary/grouping.xml
+++ b/tools/traffic_annotation/summary/grouping.xml
@@ -357,6 +357,7 @@
   </group>
   <group name="User Experience">
     <sender name="Autofill">
+      <traffic_annotation unique_id="autofill_image_fetcher_card_art_image"/>
       <traffic_annotation unique_id="autofill_query"/>
       <traffic_annotation unique_id="autofill_upload"/>
       <traffic_annotation unique_id="load_autofill_gstatic_data"/>
diff --git a/ui/accessibility/accessibility_features.cc b/ui/accessibility/accessibility_features.cc
index 51163db3..74c9fcf 100644
--- a/ui/accessibility/accessibility_features.cc
+++ b/ui/accessibility/accessibility_features.cc
@@ -91,6 +91,16 @@
 bool IsIChromeAccessibleEnabled() {
   return base::FeatureList::IsEnabled(::features::kIChromeAccessible);
 }
+
+const base::Feature kSelectiveUIAEnablement{"SelectiveUIAEnablement",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Returns true if accessibility will be selectively enabled depending on the
+// UIA APIs that are called, allowing non-screenreader usage to enable less of
+// the accessibility system.
+bool IsSelectiveUIAEnablementEnabled() {
+  return base::FeatureList::IsEnabled(::features::kSelectiveUIAEnablement);
+}
 #endif  // defined(OS_WIN)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -111,6 +121,14 @@
       ::features::kMagnifierContinuousMouseFollowingModeSetting);
 }
 
+const base::Feature kMagnifierCaretFollowingFromJavascript{
+    "MagnifierCaretFollowingFromJavascript", base::FEATURE_DISABLED_BY_DEFAULT};
+
+bool IsMagnifierCaretFollowingFromJavascriptEnabled() {
+  return base::FeatureList::IsEnabled(
+      ::features::kMagnifierCaretFollowingFromJavascript);
+}
+
 const base::Feature kEnableSwitchAccessPointScanning{
     "EnableSwitchAccessPointScanning", base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/ui/accessibility/accessibility_features.h b/ui/accessibility/accessibility_features.h
index c0793b8e..2cfd580 100644
--- a/ui/accessibility/accessibility_features.h
+++ b/ui/accessibility/accessibility_features.h
@@ -75,6 +75,13 @@
 // Returns true if the IChromeAccessible COM API is enabled.
 AX_BASE_EXPORT bool IsIChromeAccessibleEnabled();
 
+AX_BASE_EXPORT extern const base::Feature kSelectiveUIAEnablement;
+
+// Returns true if accessibility will be selectively enabled depending on the
+// UIA APIs that are called, allowing non-screenreader usage to enable less of
+// the accessibility system.
+AX_BASE_EXPORT bool IsSelectiveUIAEnablementEnabled();
+
 #endif  // defined(OS_WIN)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -94,6 +101,14 @@
 // following mode in Magnifier settings is enabled.
 AX_BASE_EXPORT bool IsMagnifierContinuousMouseFollowingModeSettingEnabled();
 
+// Enables new caret following behavior from Javascript.
+AX_BASE_EXPORT extern const base::Feature
+    kMagnifierCaretFollowingFromJavascript;
+
+// Returns true if the feature to allow new caret following from Javascript is
+// enabled.
+AX_BASE_EXPORT bool IsMagnifierCaretFollowingFromJavascriptEnabled();
+
 // Enables ability to choose point scanning mode in switch access.
 AX_BASE_EXPORT extern const base::Feature kEnableSwitchAccessPointScanning;
 
diff --git a/ui/accessibility/ax_mode.h b/ui/accessibility/ax_mode.h
index 6bac8eb..0bb0d50 100644
--- a/ui/accessibility/ax_mode.h
+++ b/ui/accessibility/ax_mode.h
@@ -121,6 +121,13 @@
                                         AXMode::kInlineTextBoxes |
                                         AXMode::kScreenReader | AXMode::kHTML);
 
+// Similar to kAXModeComplete, used when an AT that requires full accessibility
+// access, but does not need all HTML properties or attributes.
+static constexpr AXMode kAXModeCompleteNoHTML(AXMode::kNativeAPIs |
+                                              AXMode::kWebContents |
+                                              AXMode::kInlineTextBoxes |
+                                              AXMode::kScreenReader);
+
 // For debugging, test assertions, etc.
 AX_BASE_EXPORT std::ostream& operator<<(std::ostream& stream,
                                         const AXMode& mode);
diff --git a/ui/accessibility/platform/ax_fragment_root_win.cc b/ui/accessibility/platform/ax_fragment_root_win.cc
index 7a180e4..fa7624a 100644
--- a/ui/accessibility/platform/ax_fragment_root_win.cc
+++ b/ui/accessibility/platform/ax_fragment_root_win.cc
@@ -326,7 +326,7 @@
   // Automation. Signal observers when we're asked for a platform object on it.
   for (WinAccessibilityAPIUsageObserver& observer :
        GetWinAccessibilityAPIUsageObserverList()) {
-    observer.OnUIAutomationUsed();
+    observer.OnBasicUIAutomationUsed();
   }
   return platform_node_.Get();
 }
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index 6fb0a48..c727ff3 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -4185,6 +4185,7 @@
                                                      IUnknown** result) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_PATTERN_PROVIDER);
   WIN_ACCESSIBILITY_API_PERF_HISTOGRAM(UMA_API_GET_PATTERN_PROVIDER);
+  NotifyAPIObserverForPatternRequest(pattern_id);
   return GetPatternProviderImpl(pattern_id, result);
 }
 
@@ -4217,6 +4218,9 @@
     // Collapse all unknown property IDs into a single bucket.
     base::UmaHistogramSparse("Accessibility.WinAPIs.GetPropertyValue", 0);
   }
+
+  NotifyAPIObserverForPropertyRequest(property_id);
+
   return GetPropertyValueImpl(property_id, result);
 }
 
@@ -8230,6 +8234,92 @@
   SanitizeStringAttributeForIA2(input, output);
 }
 
+void AXPlatformNodeWin::NotifyAPIObserverForPatternRequest(
+    PATTERNID pattern_id) const {
+  bool probable_advanced_client_detected = false;
+  bool text_pattern_support_needed = false;
+  switch (pattern_id) {
+    case UIA_TextPatternId:
+    case UIA_TextChildPatternId:
+      // These properties require information gated behind the kInlineTextBoxes
+      // AXMode. See kInlineTextBoxes for details.
+      text_pattern_support_needed = true;
+      break;
+    // These properties require more advanced accessibility features to be
+    // enabled See kScreenReader for details.
+    case UIA_RangeValuePatternId:
+    case UIA_TableItemPatternId:
+      probable_advanced_client_detected = true;
+      break;
+  }
+
+  for (WinAccessibilityAPIUsageObserver& observer :
+       GetWinAccessibilityAPIUsageObserverList()) {
+    if (probable_advanced_client_detected)
+      observer.OnAdvancedUIAutomationUsed();
+    if (text_pattern_support_needed)
+      observer.OnTextPatternRequested();
+  }
+}
+void AXPlatformNodeWin::NotifyAPIObserverForPropertyRequest(
+    PROPERTYID property_id) const {
+  bool probable_advanced_client_detected = false;
+  bool probable_screen_reader_detected = false;
+  switch (property_id) {
+    // These properties are used by non-screenreader UIA clients. They should
+    // not cause additional enablement.
+    case UIA_ControlTypePropertyId:
+    case UIA_HasKeyboardFocusPropertyId:
+    case UIA_IsControlElementPropertyId:
+    case UIA_FrameworkIdPropertyId:
+    case UIA_IsEnabledPropertyId:
+      break;
+    //  These properties are not currently implemented and should not cause
+    //  enablement.
+    case UIA_AnnotationTypesPropertyId:
+    case UIA_CenterPointPropertyId:
+    case UIA_FillColorPropertyId:
+    case UIA_FillTypePropertyId:
+    case UIA_HeadingLevelPropertyId:
+    case UIA_ItemTypePropertyId:
+    case UIA_OutlineColorPropertyId:
+    case UIA_OutlineThicknessPropertyId:
+    case UIA_RotationPropertyId:
+    case UIA_SizePropertyId:
+    case UIA_VisualEffectsPropertyId:
+      break;
+    // These properties are provided by UIA Core; we should not implement, and
+    // they should not cause enablement.
+    case UIA_BoundingRectanglePropertyId:
+    case UIA_NativeWindowHandlePropertyId:
+    case UIA_ProcessIdPropertyId:
+    case UIA_ProviderDescriptionPropertyId:
+    case UIA_RuntimeIdPropertyId:
+      break;
+    // These properties are indicative of a screenreader, we should enable full
+    // accessibility support.
+    case UIA_AriaRolePropertyId:
+    case UIA_LabeledByPropertyId:
+    case UIA_LiveSettingPropertyId:
+    case UIA_LevelPropertyId:
+    case UIA_DescribedByPropertyId:
+      probable_screen_reader_detected = true;
+      probable_advanced_client_detected = true;
+      break;
+    default:
+      // All other properties should cause us to enable.
+      probable_advanced_client_detected = true;
+  }
+
+  for (WinAccessibilityAPIUsageObserver& observer :
+       GetWinAccessibilityAPIUsageObserverList()) {
+    if (probable_advanced_client_detected)
+      observer.OnAdvancedUIAutomationUsed();
+    if (probable_screen_reader_detected)
+      observer.OnProbableUIAutomationScreenReaderDetected();
+  }
+}
+
 // static
 void AXPlatformNodeWin::SanitizeStringAttributeForIA2(const std::string& input,
                                                       std::string* output) {
diff --git a/ui/accessibility/platform/ax_platform_node_win.h b/ui/accessibility/platform/ax_platform_node_win.h
index 84a21ed..91f114d 100644
--- a/ui/accessibility/platform/ax_platform_node_win.h
+++ b/ui/accessibility/platform/ax_platform_node_win.h
@@ -341,7 +341,10 @@
   virtual void OnIAccessible2Used() = 0;
   virtual void OnScreenReaderHoneyPotQueried() = 0;
   virtual void OnAccNameCalled() = 0;
-  virtual void OnUIAutomationUsed() = 0;
+  virtual void OnBasicUIAutomationUsed() = 0;
+  virtual void OnAdvancedUIAutomationUsed() = 0;
+  virtual void OnProbableUIAutomationScreenReaderDetected() = 0;
+  virtual void OnTextPatternRequested() = 0;
   virtual void StartFiringUIAEvents() = 0;
   virtual void EndFiringUIAEvents() = 0;
 };
@@ -1470,6 +1473,9 @@
       const std::wstring& active_composition_text,
       bool is_composition_committed);
 
+  void NotifyAPIObserverForPatternRequest(PATTERNID pattern_id) const;
+  void NotifyAPIObserverForPropertyRequest(PROPERTYID property_id) const;
+
   // Return true if the given element is valid enough to be returned as a value
   // for a UIA relation property (e.g. ControllerFor).
   static bool IsValidUiaRelationTarget(AXPlatformNode* ax_platform_node);
diff --git a/ui/android/java/res/values/attrs.xml b/ui/android/java/res/values/attrs.xml
index 9a656c4..29af8f9 100644
--- a/ui/android/java/res/values/attrs.xml
+++ b/ui/android/java/res/values/attrs.xml
@@ -11,6 +11,7 @@
     <!-- Material roles currently only used in downstream. Define them here
          so public builds do not complain the attribute is not found. Removal
          tracked in https://crbug.com/1216204. -->
+    <attr name="colorOnPrimaryContainer" format="color"/>
     <attr name="colorOnSurfaceVariant" format="color"/>
     <attr name="colorOnSurfaceInverse" format="color"/>
     <attr name="elevationOverlayAccentColor" format="color"/>
diff --git a/ui/android/java/res/values/color_palette.xml b/ui/android/java/res/values/color_palette.xml
index 691686d..54a25a3 100644
--- a/ui/android/java/res/values/color_palette.xml
+++ b/ui/android/java/res/values/color_palette.xml
@@ -10,12 +10,14 @@
     <color name="baseline_primary_200">#A8C7FA</color>
     <color name="baseline_primary_600">#0B57D0</color>
     <color name="baseline_primary_800">#062E6F</color>
+    <color name="baseline_primary_900">#041E49</color>
     <color name="baseline_neutral_100">#E3E3E3</color>
     <color name="baseline_neutral_100_alpha_12">#1EE3E3E3</color>
     <color name="baseline_neutral_900_alpha_12">#1E1F1F1F</color>
     <color name="baseline_neutral_900_with_neutral_100_alpha_38">#696969</color>
     <color name="baseline_neutral_variant_100">#E1E3E1</color>
     <color name="baseline_neutral_variant_200">#C4C7C5</color>
+    <color name="baseline_neutral_variant_200_alpha_15">#26C4C7C5</color>
     <color name="baseline_neutral_variant_400">#8E918F</color>
 
     <color name="baseline_neutral_900_with_neutral_200_alpha_12_with_primary_200_alpha_2">#353637</color>
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 76f3bfb3..898f329 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -442,10 +442,10 @@
 
   deps = [
     ":locales_list",
+    "//base",
     "//base:base_static",
     "//base:i18n",
     "//base/third_party/dynamic_annotations",
-    "//base/util/type_safety:type_safety",
     "//build:chromeos_buildflags",
     "//net",
     "//third_party/brotli:dec",
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 4e1b6b7..315d7455 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -103,6 +103,7 @@
     "bubble/info_bubble.h",
     "bubble/tooltip_icon.h",
     "button_drag_utils.h",
+    "cascading_property.h",
     "color_chooser/color_chooser_listener.h",
     "context_menu_controller.h",
     "controls/button/button.h",
@@ -324,6 +325,7 @@
     "bubble/info_bubble.cc",
     "bubble/tooltip_icon.cc",
     "button_drag_utils.cc",
+    "cascading_property.cc",
     "context_menu_controller.cc",
     "controls/button/button.cc",
     "controls/button/button_controller.cc",
diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc
index 41e9c83..5549091 100644
--- a/ui/views/bubble/bubble_frame_view.cc
+++ b/ui/views/bubble/bubble_frame_view.cc
@@ -311,6 +311,13 @@
 
 void BubbleFrameView::SizeConstraintsChanged() {}
 
+void BubbleFrameView::InsertClientView(ClientView* client_view) {
+  // Place the client view before any footnote view for focus order.
+  footnote_container_
+      ? AddChildViewAt(client_view, GetIndexOf(footnote_container_))
+      : AddChildView(client_view);
+}
+
 void BubbleFrameView::SetTitleView(std::unique_ptr<View> title_view) {
   DCHECK(title_view);
   delete default_title_;
@@ -555,13 +562,9 @@
   delete footnote_container_;
   footnote_container_ = nullptr;
   if (view) {
-    // Insert the footnote container before |close_| so that the footnote is
-    // inserted before caption buttons in the focus cycle.
     int radius = bubble_border_ ? bubble_border_->corner_radius() : 0;
-    footnote_container_ =
-        AddChildViewAt(std::make_unique<FootnoteContainerView>(
-                           footnote_margins_, std::move(view), radius),
-                       GetIndexOf(close_));
+    footnote_container_ = AddChildView(std::make_unique<FootnoteContainerView>(
+        footnote_margins_, std::move(view), radius));
   }
   InvalidateLayout();
 }
diff --git a/ui/views/bubble/bubble_frame_view.h b/ui/views/bubble/bubble_frame_view.h
index 515987d..c526a05 100644
--- a/ui/views/bubble/bubble_frame_view.h
+++ b/ui/views/bubble/bubble_frame_view.h
@@ -64,6 +64,7 @@
   void UpdateWindowIcon() override;
   void UpdateWindowTitle() override;
   void SizeConstraintsChanged() override;
+  void InsertClientView(ClientView* client_view) override;
 
   // Sets a custom view to be the dialog title instead of the |default_title_|
   // label. If there is an existing title view it will be deleted.
diff --git a/ui/views/cascading_property.cc b/ui/views/cascading_property.cc
new file mode 100644
index 0000000..d69074f
--- /dev/null
+++ b/ui/views/cascading_property.cc
@@ -0,0 +1,65 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/cascading_property.h"
+#include "ui/base/theme_provider.h"
+#include "ui/native_theme/native_theme.h"
+
+DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT,
+                                       views::CascadingProperty<SkColor>*)
+
+namespace views {
+
+namespace {
+class CascadingThemeProviderColor final : public CascadingProperty<SkColor> {
+ public:
+  explicit CascadingThemeProviderColor(int color_id) : color_id_(color_id) {}
+
+  // CascadingProperty<SkColor>:
+  SkColor GetValue(const View* view) const override {
+    return view->GetThemeProvider()->GetColor(color_id_);
+  }
+
+ private:
+  const int color_id_;
+};
+
+class CascadingNativeThemeColor final : public CascadingProperty<SkColor> {
+ public:
+  explicit CascadingNativeThemeColor(ui::NativeTheme::ColorId color_id)
+      : color_id_(color_id) {}
+
+  // CascadingProperty<SkColor>:
+  SkColor GetValue(const View* view) const override {
+    return view->GetNativeTheme()->GetSystemColor(color_id_);
+  }
+
+ private:
+  const ui::NativeTheme::ColorId color_id_;
+};
+}  // namespace
+
+DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(CascadingProperty<SkColor>,
+                                   kCascadingBackgroundColor,
+                                   nullptr)
+
+void SetCascadingThemeProviderColor(
+    views::View* view,
+    const ui::ClassProperty<CascadingProperty<SkColor>*>* property_key,
+    int color_id) {
+  SetCascadingProperty(
+      view, property_key,
+      std::make_unique<views::CascadingThemeProviderColor>(color_id));
+}
+
+void SetCascadingNativeThemeColor(
+    views::View* view,
+    const ui::ClassProperty<CascadingProperty<SkColor>*>* property_key,
+    ui::NativeTheme::ColorId color_id) {
+  SetCascadingProperty(
+      view, property_key,
+      std::make_unique<views::CascadingNativeThemeColor>(color_id));
+}
+
+}  // namespace views
diff --git a/ui/views/cascading_property.h b/ui/views/cascading_property.h
new file mode 100644
index 0000000..bb4e685
--- /dev/null
+++ b/ui/views/cascading_property.h
@@ -0,0 +1,82 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_CASCADING_PROPERTY_H_
+#define UI_VIEWS_CASCADING_PROPERTY_H_
+
+#include <memory>
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/class_property.h"
+#include "ui/native_theme/native_theme.h"
+#include "ui/views/view.h"
+#include "ui/views/views_export.h"
+
+namespace views {
+
+class View;
+
+template <typename T>
+class CascadingProperty {
+ public:
+  CascadingProperty() = default;
+  CascadingProperty(const CascadingProperty&) = delete;
+  CascadingProperty& operator=(const CascadingProperty&) = delete;
+  virtual ~CascadingProperty() = default;
+
+  virtual T GetValue(const View* view) const = 0;
+};
+
+template <typename T>
+const CascadingProperty<T>* GetCascadingPropertyObject(
+    View* view,
+    const ui::ClassProperty<CascadingProperty<T>*>* property_key) {
+  const CascadingProperty<T>* property = view->GetProperty(property_key);
+  if (property != nullptr)
+    return property;
+  if (!view->parent())
+    return nullptr;
+  return GetCascadingPropertyObject(view->parent(), property_key);
+}
+
+template <typename T>
+absl::optional<T> GetCascadingProperty(
+    View* view,
+    const ui::ClassProperty<CascadingProperty<T>*>* property_key) {
+  const CascadingProperty<T>* property =
+      GetCascadingPropertyObject(view, property_key);
+  return property ? absl::optional<T>(property->GetValue(view)) : absl::nullopt;
+}
+
+template <typename T, typename K>
+void SetCascadingProperty(
+    View* view,
+    const ui::ClassProperty<CascadingProperty<T>*>* property_key,
+    std::unique_ptr<K> property) {
+  // TODO(pbos): See if there could be a way to (D)CHECK that property_key is
+  // actually owned.
+  view->SetProperty(property_key,
+                    static_cast<CascadingProperty<T>*>(property.release()));
+}
+
+VIEWS_EXPORT void SetCascadingThemeProviderColor(
+    views::View* view,
+    const ui::ClassProperty<CascadingProperty<SkColor>*>* property_key,
+    int color_id);
+
+VIEWS_EXPORT void SetCascadingNativeThemeColor(
+    views::View* view,
+    const ui::ClassProperty<CascadingProperty<SkColor>*>* property_key,
+    ui::NativeTheme::ColorId color_id);
+
+VIEWS_EXPORT extern const ui::ClassProperty<CascadingProperty<SkColor>*>* const
+    kCascadingBackgroundColor;
+
+}  // namespace views
+
+DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT,
+                                        views::CascadingProperty<SkColor>*)
+
+#endif  // UI_VIEWS_CASCADING_PROPERTY_H_
diff --git a/ui/views/controls/focus_ring.cc b/ui/views/controls/focus_ring.cc
index e93896a..0de6ff1 100644
--- a/ui/views/controls/focus_ring.cc
+++ b/ui/views/controls/focus_ring.cc
@@ -10,6 +10,7 @@
 #include "base/i18n/rtl.h"
 #include "base/memory/ptr_util.h"
 #include "base/notreached.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -19,6 +20,7 @@
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/skia_util.h"
+#include "ui/views/cascading_property.h"
 #include "ui/views/controls/focusable_border.h"
 #include "ui/views/controls/highlight_path_generator.h"
 #include "ui/views/style/platform_style.h"
@@ -31,7 +33,6 @@
 
 namespace {
 
-DEFINE_UI_CLASS_PROPERTY_KEY(int, kFocusRingBackgroundColorIdKey, -1)
 DEFINE_UI_CLASS_PROPERTY_KEY(FocusRing*, kFocusRingIdKey, nullptr)
 
 bool IsPathUsable(const SkPath& path) {
@@ -44,20 +45,11 @@
                : ui::NativeTheme::kColorId_AlertSeverityHigh;
 }
 
-int GetBackgroundColorId(View* view) {
-  int color_id_property = view->GetProperty(kFocusRingBackgroundColorIdKey);
-  if (color_id_property != -1)
-    return color_id_property;
-  if (!view->parent())
-    return -1;
-  return GetBackgroundColorId(view->parent());
-}
-
 SkColor GetBackgroundColor(View* view) {
-  int color_id = GetBackgroundColorId(view);
-  return color_id == -1 ? view->GetNativeTheme()->GetSystemColor(
-                              ui::NativeTheme::kColorId_WindowBackground)
-                        : view->GetThemeProvider()->GetColor(color_id);
+  const absl::optional<SkColor> color =
+      GetCascadingProperty(view, kCascadingBackgroundColor);
+  return color.value_or(view->GetNativeTheme()->GetSystemColor(
+      ui::NativeTheme::kColorId_WindowBackground));
 }
 
 SkColor GetColor(View* focus_ring, bool valid) {
@@ -123,11 +115,6 @@
   host->ClearProperty(kFocusRingIdKey);
 }
 
-void FocusRing::SetBackgroundColorIdForSubtree(View* view,
-                                               int background_color_id) {
-  view->SetProperty(kFocusRingBackgroundColorIdKey, background_color_id);
-}
-
 FocusRing::~FocusRing() = default;
 
 void FocusRing::SetPathGenerator(
diff --git a/ui/views/controls/focus_ring.h b/ui/views/controls/focus_ring.h
index 5d7514e..ba83ff43 100644
--- a/ui/views/controls/focus_ring.h
+++ b/ui/views/controls/focus_ring.h
@@ -43,21 +43,6 @@
   // Removes the FocusRing, if present, from `host`.
   static void Remove(View* host);
 
-  // Configures `view` so that FocusRings under it are aware of the background
-  // they are painted against. Unless the color of the FocusRing has been
-  // explicitly set, a color will be chosen that contrasts well against
-  // `background_color_id`.
-  // Warning: The FocusRing ThemeProvider is queried for these IDs, do not use
-  // NativeTheme color IDs here.
-  // WARNING: This is temporary shenanigans to solve an accessibility problem.
-  // DO NOT COPY this pattern or its implementation to other places in its
-  // current state.
-  // TODO(pbos): This seems not directly related to the FocusRing anymore,
-  // perhaps we could inform the view of what background color it's being
-  // painted onto orthogonally to how FocusRing uses it.
-  static void SetBackgroundColorIdForSubtree(View* view,
-                                             int background_color_id);
-
   ~FocusRing() override;
 
   // Sets the HighlightPathGenerator to draw this FocusRing around.
diff --git a/ui/views/controls/label.cc b/ui/views/controls/label.cc
index 3c6d1d0e..79a26da9 100644
--- a/ui/views/controls/label.cc
+++ b/ui/views/controls/label.cc
@@ -34,6 +34,7 @@
 #include "ui/native_theme/native_theme.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/background.h"
+#include "ui/views/cascading_property.h"
 #include "ui/views/controls/menu/menu_runner.h"
 #include "ui/views/focus/focus_manager.h"
 #include "ui/views/native_cursor.h"
@@ -61,6 +62,10 @@
 
 namespace views {
 
+DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(CascadingProperty<SkColor>,
+                                   kCascadingLabelEnabledColor,
+                                   nullptr)
+
 Label::Label() : Label(std::u16string()) {}
 
 Label::Label(const std::u16string& text)
@@ -1177,8 +1182,10 @@
 void Label::UpdateColorsFromTheme() {
   ui::NativeTheme* theme = GetNativeTheme();
   if (!enabled_color_set_) {
-    requested_enabled_color_ =
-        style::GetColor(*this, text_context_, text_style_);
+    const absl::optional<SkColor> cascading_color =
+        GetCascadingProperty(this, kCascadingLabelEnabledColor);
+    requested_enabled_color_ = cascading_color.value_or(
+        style::GetColor(*this, text_context_, text_style_));
   }
   if (!background_color_set_) {
     background_color_ =
diff --git a/ui/views/controls/label.h b/ui/views/controls/label.h
index 0e8b88d..5bad0a8c 100644
--- a/ui/views/controls/label.h
+++ b/ui/views/controls/label.h
@@ -17,11 +17,13 @@
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/render_text.h"
 #include "ui/gfx/text_constants.h"
+#include "ui/views/cascading_property.h"
 #include "ui/views/context_menu_controller.h"
 #include "ui/views/metadata/view_factory.h"
 #include "ui/views/selection_controller_delegate.h"
 #include "ui/views/style/typography.h"
 #include "ui/views/view.h"
+#include "ui/views/views_export.h"
 #include "ui/views/word_lookup_client.h"
 
 namespace views {
@@ -29,6 +31,9 @@
 class MenuRunner;
 class SelectionController;
 
+VIEWS_EXPORT extern const ui::ClassProperty<CascadingProperty<SkColor>*>* const
+    kCascadingLabelEnabledColor;
+
 // A view subclass that can display a string.
 class VIEWS_EXPORT Label : public View,
                            public ContextMenuController,
diff --git a/ui/views/window/non_client_view.cc b/ui/views/window/non_client_view.cc
index 2d0e850..299191b5 100644
--- a/ui/views/window/non_client_view.cc
+++ b/ui/views/window/non_client_view.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/containers/cxx20_erase.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/hit_test.h"
@@ -125,6 +126,24 @@
     client_view->SetClipPath(client_clip);
 }
 
+View::Views NonClientFrameView::GetChildrenInZOrder() {
+  View::Views paint_order = View::GetChildrenInZOrder();
+  views::ClientView* client_view =
+      GetWidget() ? GetWidget()->client_view() : nullptr;
+
+  // Move the client view to the beginning of the Z-order to ensure that the
+  // other children of the frame view draw on top of it.
+  if (client_view && base::Erase(paint_order, client_view)) {
+    paint_order.insert(paint_order.begin(), client_view);
+  }
+
+  return paint_order;
+}
+
+void NonClientFrameView::InsertClientView(ClientView* client_view) {
+  AddChildView(client_view);
+}
+
 NonClientFrameView::NonClientFrameView() {
   SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
 }
@@ -158,7 +177,7 @@
   frame_view_ = std::move(frame_view);
   if (parent()) {
     AddChildViewAt(frame_view_.get(), 0);
-    frame_view_->AddChildViewAt(client_view_, 0);
+    frame_view_->InsertClientView(client_view_);
   }
 
   if (old_frame_view)
@@ -289,7 +308,7 @@
   // constructor.
   if (details.is_add && GetWidget() && details.child == this) {
     AddChildViewAt(frame_view_.get(), 0);
-    frame_view_->AddChildViewAt(client_view_, 0);
+    frame_view_->InsertClientView(client_view_);
     if (overlay_view_)
       AddChildView(overlay_view_);
   }
diff --git a/ui/views/window/non_client_view.h b/ui/views/window/non_client_view.h
index 932a530..ab99cda 100644
--- a/ui/views/window/non_client_view.h
+++ b/ui/views/window/non_client_view.h
@@ -101,6 +101,12 @@
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   void OnThemeChanged() override;
   void Layout() override;
+  Views GetChildrenInZOrder() override;
+
+  // Inserts the passed client view into this NonClientFrameView. Subclasses can
+  // override this method to indicate a specific insertion spot for the client
+  // view.
+  virtual void InsertClientView(ClientView* client_view);
 
  private:
 #if defined(OS_WIN)
diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn
index bd5d27f..d40afb7 100644
--- a/ui/webui/resources/BUILD.gn
+++ b/ui/webui/resources/BUILD.gn
@@ -258,6 +258,7 @@
   js_files = [
     "cr_components/managed_dialog/managed_dialog.js",
     "cr_elements/action_link_css.m.js",
+    "cr_elements/cr_a11y_announcer/cr_a11y_announcer.js",
     "cr_elements/cr_actionable_row_style.m.js",
     "cr_elements/cr_fingerprint/cr_fingerprint_icon.m.js",
     "cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.m.js",
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_config.js b/ui/webui/resources/cr_components/chromeos/network/network_config.js
index 8ce3f50..a61b6df2 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_config.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_config.js
@@ -1582,7 +1582,7 @@
    */
   setPropertiesCallback_(success, errorMessage, connect) {
     if (!success) {
-      console.error(
+      console.warn(
           'Unable to set properties for: ' + this.guid +
           ' Error: ' + errorMessage);
       this.propertiesSent_ = false;
@@ -1609,7 +1609,7 @@
    */
   createNetworkCallback_(guid, errorMessage, connect) {
     if (!guid) {
-      console.error(
+      console.warn(
           'Unable to configure network: ' + guid + ' Error: ' + errorMessage);
       this.propertiesSent_ = false;
       this.setError_(errorMessage);
@@ -1641,7 +1641,7 @@
         return;
       }
       this.setError_(response.message);
-      console.error(
+      console.warn(
           'Error connecting to network: ' + guid + ': ' + result.toString() +
           ' Message: ' + response.message);
       this.propertiesSent_ = false;
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_password_input.html b/ui/webui/resources/cr_components/chromeos/network/network_password_input.html
index 2b06e2f..58cf1e2 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_password_input.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_password_input.html
@@ -60,7 +60,7 @@
             </cr-icon-button>
             <paper-tooltip id="passwordVisibilityTooltip"
                 for="icon"
-                position="left"
+                position="[[tooltipPosition_]]"
                 fit-to-visible-bounds role="tooltip">
               [[getShowPasswordTitle_(showPassword)]]
             </paper-tooltip>
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_password_input.js b/ui/webui/resources/cr_components/chromeos/network/network_password_input.js
index 71f0c96..5a29023 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_password_input.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_password_input.js
@@ -58,6 +58,13 @@
       value: '',
     },
 
+    /** @private */
+    tooltipPosition_: {
+      type: String,
+      value: '',
+    },
+
+    /** @private */
     showPolicyIndicator_: {
       type: Boolean,
       value: false,
@@ -65,6 +72,12 @@
     },
   },
 
+  /** @override */
+  attached() {
+    this.tooltipPosition_ =
+        window.getComputedStyle(this).direction === 'rtl' ? 'right' : 'left';
+  },
+
   /** @private */
   focus() {
     this.$$('cr-input').focus();
diff --git a/ui/webui/resources/cr_elements/BUILD.gn b/ui/webui/resources/cr_elements/BUILD.gn
index fa45fca..43197c4 100644
--- a/ui/webui/resources/cr_elements/BUILD.gn
+++ b/ui/webui/resources/cr_elements/BUILD.gn
@@ -155,6 +155,7 @@
   out_manifest = "$target_gen_dir/$preprocess_gen_manifest"
   in_files = [
     "action_link_css.m.js",
+    "cr_a11y_announcer/cr_a11y_announcer.js",
     "cr_actionable_row_style.m.js",
     "cr_action_menu/cr_action_menu.m.js",
     "cr_button/cr_button.m.js",
@@ -251,6 +252,7 @@
 
     # Targets for auto-generated Polymer 3 JS Modules
     ":cr_elements_module_resources",
+    "cr_a11y_announcer:closure_compile",
     "cr_action_menu:closure_compile_module",
     "cr_button:closure_compile_module",
     "cr_checkbox:closure_compile_module",
@@ -379,6 +381,7 @@
     ":shared_style_css_module",
     ":shared_vars_css_module",
     ":web_components",
+    "cr_a11y_announcer:web_components",
     "cr_action_menu:cr_action_menu_module",
     "cr_button:cr_button_module",
     "cr_checkbox:cr_checkbox_module",
diff --git a/ui/webui/resources/cr_elements/cr_a11y_announcer/BUILD.gn b/ui/webui/resources/cr_elements/cr_a11y_announcer/BUILD.gn
new file mode 100644
index 0000000..855a763f
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_a11y_announcer/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/polymer/html_to_js.gni")
+
+html_to_js("web_components") {
+  js_files = [ "cr_a11y_announcer.js" ]
+}
+
+js_type_check("closure_compile") {
+  is_polymer3 = true
+  deps = [ ":cr_a11y_announcer" ]
+}
+
+js_library("cr_a11y_announcer") {
+  deps = [
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:assert.m",
+  ]
+}
diff --git a/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.html b/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.html
new file mode 100644
index 0000000..05f70b2
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.html
@@ -0,0 +1,11 @@
+<style>
+  :host {
+    clip: rect(0 0 0 0);
+    height: 1px;
+    overflow: hidden;
+    position: absolute;
+    width: 1px;
+  }
+</style>
+
+<div id="messages" role="alert" aria-live="polite"></div>
diff --git a/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js b/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js
new file mode 100644
index 0000000..22116d2
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js
@@ -0,0 +1,113 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+/**
+ * The CrA11yAnnouncerElement is a visually hidden element that reads out
+ * messages to a screen reader. This is preferred over IronA11yAnnouncer.
+ * @fileoverview
+ */
+
+/**
+ * 150ms seems to be around the minimum time required for screen readers to
+ * read out consecutively queued messages.
+ * @type {number}
+ */
+export const TIMEOUT_MS = 150;
+
+/**
+ * A map of an HTML element to its corresponding CrA11yAnnouncerElement. There
+ * may be multiple CrA11yAnnouncerElements on a page, especially for cases in
+ * which the DocumentElement's CrA11yAnnouncerElement becomes hidden or
+ * deactivated (eg. when a modal dialog causes the CrA11yAnnouncerElement to
+ * become inaccessible).
+ * @type {!Map<!HTMLElement, !CrA11yAnnouncerElement>}
+ */
+const instances = new Map();
+
+export class CrA11yAnnouncerElement extends PolymerElement {
+  static get is() {
+    return 'cr-a11y-announcer';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  constructor() {
+    super();
+
+    /** @private {?number} */
+    this.currentTimeout_ = null;
+
+    /** @private {!Array<string>} */
+    this.messages_ = [];
+  }
+
+  disconnectedCallback() {
+    super.disconnectedCallback();
+    if (this.currentTimeout_ !== null) {
+      clearTimeout(this.currentTimeout_);
+      this.currentTimeout_ = null;
+    }
+
+    for (const [parent, instance] of instances) {
+      if (instance === this) {
+        instances.delete(parent);
+        break;
+      }
+    }
+  }
+
+  /** @param {string} message */
+  announce(message) {
+    if (this.currentTimeout_ !== null) {
+      clearTimeout(this.currentTimeout_);
+      this.currentTimeout_ = null;
+    }
+
+    this.messages_.push(message);
+
+    this.currentTimeout_ = setTimeout(() => {
+      const messagesDiv = this.shadowRoot.querySelector('#messages');
+      messagesDiv.innerHTML = '';
+
+      // <if expr="is_macosx">
+      // VoiceOver on Mac does not seem to consistently read out the contents of
+      // a static alert element. Toggling the role of alert seems to force VO
+      // to consistently read out the messages.
+      messagesDiv.removeAttribute('role');
+      messagesDiv.setAttribute('role', 'alert');
+      // </if>
+
+      for (const message of this.messages_) {
+        const div = document.createElement('div');
+        div.textContent = message;
+        messagesDiv.appendChild(div);
+      }
+
+      this.messages_.length = 0;
+      this.currentTimeout_ = null;
+    }, TIMEOUT_MS);
+  }
+
+  /**
+   * @param {!HTMLElement=} container
+   * @return {!CrA11yAnnouncerElement}
+   */
+  static getInstance(container = document.body) {
+    if (instances.has(container)) {
+      return instances.get(container);
+    }
+    assert(container.isConnected);
+    const instance = new CrA11yAnnouncerElement();
+    container.appendChild(instance);
+    instances.set(container, instance);
+    return instance;
+  }
+}
+
+customElements.define(CrA11yAnnouncerElement.is, CrA11yAnnouncerElement);
diff --git a/weblayer/browser/weblayer_security_blocking_page_factory.cc b/weblayer/browser/weblayer_security_blocking_page_factory.cc
index a2a3984..7b30e247 100644
--- a/weblayer/browser/weblayer_security_blocking_page_factory.cc
+++ b/weblayer/browser/weblayer_security_blocking_page_factory.cc
@@ -231,6 +231,14 @@
   return page;
 }
 
+std::unique_ptr<security_interstitials::HttpsOnlyModeBlockingPage>
+WebLayerSecurityBlockingPageFactory::CreateHttpsOnlyModeBlockingPage(
+    content::WebContents* web_contents,
+    const GURL& request_url) {
+  // HTTPS-only mode is not implemented for weblayer.
+  return nullptr;
+}
+
 #if defined(OS_ANDROID)
 // static
 GURL WebLayerSecurityBlockingPageFactory::
diff --git a/weblayer/browser/weblayer_security_blocking_page_factory.h b/weblayer/browser/weblayer_security_blocking_page_factory.h
index e154d43..7d1a1b67 100644
--- a/weblayer/browser/weblayer_security_blocking_page_factory.h
+++ b/weblayer/browser/weblayer_security_blocking_page_factory.h
@@ -11,6 +11,7 @@
 #include "components/security_interstitials/content/bad_clock_blocking_page.h"
 #include "components/security_interstitials/content/blocked_interception_blocking_page.h"
 #include "components/security_interstitials/content/captive_portal_blocking_page.h"
+#include "components/security_interstitials/content/https_only_mode_blocking_page.h"
 #include "components/security_interstitials/content/insecure_form_blocking_page.h"
 #include "components/security_interstitials/content/mitm_software_blocking_page.h"
 #include "components/security_interstitials/content/security_blocking_page_factory.h"
@@ -77,6 +78,9 @@
   std::unique_ptr<security_interstitials::InsecureFormBlockingPage>
   CreateInsecureFormBlockingPage(content::WebContents* web_contents,
                                  const GURL& request_url) override;
+  std::unique_ptr<security_interstitials::HttpsOnlyModeBlockingPage>
+  CreateHttpsOnlyModeBlockingPage(content::WebContents* web_contents,
+                                  const GURL& request_url) override;
 
 #if defined(OS_ANDROID)
   // Returns the URL that will be navigated to when the user clicks on the